agora inbox for [email protected]help / color / mirror / Atom feed
Improved Cost Calculation for IndexOnlyScan 1267+ messages / 3 participants [nested] [flat]
* Improved Cost Calculation for IndexOnlyScan @ 2020-09-29 07:06 Hamid Akhtar <[email protected]> 0 siblings, 1 reply; 1267+ messages in thread From: Hamid Akhtar @ 2020-09-29 07:06 UTC (permalink / raw) To: PostgreSQL Hackers <[email protected]> In one of my earlier emails [1], I mentioned that there seems to be a problem with how the cost for index only scans is being calculated. [1] https://www.postgresql.org/message-id/CANugjhsnh0OBMOYc7qKcC%2BZsVvAXCeF7QiidLuFvg6zmHy1C7A%40mail.g... My concern is that there seems to be a bigger disconnect between the cost of index only scan and the execution time. Having tested this on 3 different systems, docker, laptop and a server with RAID 5 SSD configured, at the threshold where index only scan cost exceeds that of sequential scan, index only is still around 30% faster than the sequential scan. My initial hunch was that perhaps we need to consider a different approach when considering cost for index only scan. However, the solution seems somewhat simple. cost_index function in costsize.c, in case of indexonlyscan, multiplies the number of pages fetched by a factor of (1.0 - baserel->allvisfrac) which is then used to calculate the max_IO_cost and min_IO_cost. This is very similar to the cost estimate methods for indexes internally call genericostesimate function. This function primarily gets the number of pages for the indexes and multiplies that with random page cost (spc_random_page_cost) to get the total disk access cost. I believe that in case of index only scan, we should adjust the spc_random_page_cost in context of baserel->allvisfrac so that it accounts for random pages for only the fraction that needs to be read for the relation and excludes that the index page fetches. With the attached POC for this approach (based on the current master branch), index only scan which was previously bailing out at much earlier, approximately around when 50% of the index/table was being scanned. With the attached patch, this percentage goes upto 70%. The execution time for index only still remains well below that of the sequential scan, however, this is a significant improvement IMHO. Following is the simple SQL that I was using to see how this patch impacts that indexonlyscan costing and execution time for the scan. You may tweak the table size or the number of rows being scanned for your environment.. SQL: ----- CREATE TABLE index_test AS (SELECT GENERATE_SERIES(1, 1000000)::int id, 'hello world' AS name); CREATE INDEX on index_test(id); VACUUM ANALYZE index_test; EXPLAIN ANALYZE SELECT COUNT(*) FROM index_test WHERE id < 7000000; -- Highgo Software (Canada/China/Pakistan) URL : www.highgo.ca ADDR: 10318 WHALLEY BLVD, Surrey, BC CELL:+923335449950 EMAIL: mailto:[email protected] SKYPE: engineeredvirus Attachments: [application/octet-stream] poc_indexonly_costing.patch (2.5K, ../../CANugjhuT6qxvvC9N6WEq4FfLCa94MNeG5sfOuG=b4sGpYKt2hQ@mail.gmail.com/3-poc_indexonly_costing.patch) download | inline diff: diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 00c7afc66f..6550cfcc94 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -6003,6 +6003,8 @@ genericcostestimate(PlannerInfo *root, GenericCosts *costs) { IndexOptInfo *index = path->indexinfo; + RelOptInfo *baserel = index->rel; + bool indexonly = (path->path.pathtype == T_IndexOnlyScan); List *indexQuals = get_quals_from_indexclauses(path->indexclauses); List *indexOrderBys = path->indexorderbys; Cost indexStartupCost; @@ -6011,7 +6013,9 @@ genericcostestimate(PlannerInfo *root, double indexCorrelation; double numIndexPages; double numIndexTuples; + double spc_seq_page_cost; double spc_random_page_cost; + double index_random_page_cost; double num_sa_scans; double num_outer_scans; double num_scans; @@ -6102,7 +6106,23 @@ genericcostestimate(PlannerInfo *root, /* fetch estimated page cost for tablespace containing index */ get_tablespace_page_costs(index->reltablespace, &spc_random_page_cost, - NULL); + &spc_seq_page_cost); + + /* If it is an index only scan, random page cost should be incurred for + * the percentage of pages that need to be fetched from disk for the + * relation. Considering that, random page cost should be adjusted such + * that when no tuples are to be fetched from the table, we end up with a + * sequential page cost, otherwise, we hover betwen that and random + * page cost for the tablespace. + * + * Otherwise, we default to the random page cost for table space. + */ + if (indexonly) + index_random_page_cost = Min(spc_seq_page_cost + + spc_random_page_cost * (1.0 - baserel->allvisfrac), + spc_random_page_cost); + else + index_random_page_cost = spc_random_page_cost; /* * Now compute the disk access costs. @@ -6142,16 +6162,16 @@ genericcostestimate(PlannerInfo *root, * share for each outer scan. (Don't pro-rate for ScalarArrayOpExpr, * since that's internal to the indexscan.) */ - indexTotalCost = (pages_fetched * spc_random_page_cost) + indexTotalCost = (pages_fetched * index_random_page_cost) / num_outer_scans; } else { /* - * For a single index scan, we just charge spc_random_page_cost per + * For a single index scan, we just charge random page cost per * page touched. */ - indexTotalCost = numIndexPages * spc_random_page_cost; + indexTotalCost = numIndexPages * index_random_page_cost; } /* ^ permalink raw reply [nested|flat] 1267+ messages in thread
* Re: Improved Cost Calculation for IndexOnlyScan @ 2020-09-29 08:08 Heikki Linnakangas <[email protected]> parent: Hamid Akhtar <[email protected]> 0 siblings, 1 reply; 1267+ messages in thread From: Heikki Linnakangas @ 2020-09-29 08:08 UTC (permalink / raw) To: Hamid Akhtar <[email protected]>; PostgreSQL Hackers <[email protected]> On 29/09/2020 10:06, Hamid Akhtar wrote: > In one of my earlier emails [1], I mentioned that there seems to be a > problem with how the cost for index only scans is being calculated. > [1] > https://www.postgresql.org/message-id/CANugjhsnh0OBMOYc7qKcC%2BZsVvAXCeF7QiidLuFvg6zmHy1C7A%40mail.g... > > My concern is that there seems to be a bigger disconnect between the > cost of index only scan and the execution time. Having tested this on 3 > different systems, docker, laptop and a server with RAID 5 SSD > configured, at the threshold where index only scan cost exceeds that of > sequential scan, index only is still around 30% faster than the > sequential scan. A 30% discrepancy doesn't sound too bad, to be honest. The exact threshold depends on so many factors. > My initial hunch was that perhaps we need to consider a different > approach when considering cost for index only scan. However, the > solution seems somewhat simple. > > cost_index function in costsize.c, in case of indexonlyscan, multiplies > the number of pages fetched by a factor of (1.0 - baserel->allvisfrac) > which is then used to calculate the max_IO_cost and min_IO_cost. > > This is very similar to the cost estimate methods for indexes internally > call genericostesimate function. This function primarily gets the number > of pages for the indexes and multiplies that with random page cost > (spc_random_page_cost) to get the total disk access cost. > > I believe that in case of index only scan, we should adjust the > spc_random_page_cost in context of baserel->allvisfrac so that it > accounts for random pages for only the fraction that needs to be read > for the relation and excludes that the index page fetches. That doesn't sound right to me. The genericcostestimate() function calculates the number of *index* pages fetched. It makes no difference if it's an Index Scan or an Index Only Scan. genericcostestimate() could surely be made smarter. Currently, it multiplies the number of index pages fetched with random_page_cost, even though a freshly created index is mostly physically ordered by the keys. seq_page_cost with some fudge factor might be more appropriate, whether or not it's an Index Only Scan. Not sure what the exact formula should be, just replacing random_page_cost with seq_page_cost is surely not right either. - Heikki ^ permalink raw reply [nested|flat] 1267+ messages in thread
* Re: Improved Cost Calculation for IndexOnlyScan @ 2020-09-29 08:49 Hamid Akhtar <[email protected]> parent: Heikki Linnakangas <[email protected]> 0 siblings, 1 reply; 1267+ messages in thread From: Hamid Akhtar @ 2020-09-29 08:49 UTC (permalink / raw) To: Heikki Linnakangas <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On Tue, Sep 29, 2020 at 1:08 PM Heikki Linnakangas <[email protected]> wrote: > On 29/09/2020 10:06, Hamid Akhtar wrote: > > In one of my earlier emails [1], I mentioned that there seems to be a > > problem with how the cost for index only scans is being calculated. > > [1] > > > https://www.postgresql.org/message-id/CANugjhsnh0OBMOYc7qKcC%2BZsVvAXCeF7QiidLuFvg6zmHy1C7A%40mail.g... > > > > My concern is that there seems to be a bigger disconnect between the > > cost of index only scan and the execution time. Having tested this on 3 > > different systems, docker, laptop and a server with RAID 5 SSD > > configured, at the threshold where index only scan cost exceeds that of > > sequential scan, index only is still around 30% faster than the > > sequential scan. > > A 30% discrepancy doesn't sound too bad, to be honest. The exact > threshold depends on so many factors. > > > My initial hunch was that perhaps we need to consider a different > > approach when considering cost for index only scan. However, the > > solution seems somewhat simple. > > > > cost_index function in costsize.c, in case of indexonlyscan, multiplies > > the number of pages fetched by a factor of (1.0 - baserel->allvisfrac) > > which is then used to calculate the max_IO_cost and min_IO_cost. > > > > This is very similar to the cost estimate methods for indexes internally > > call genericostesimate function. This function primarily gets the number > > of pages for the indexes and multiplies that with random page cost > > (spc_random_page_cost) to get the total disk access cost. > > > > I believe that in case of index only scan, we should adjust the > > spc_random_page_cost in context of baserel->allvisfrac so that it > > accounts for random pages for only the fraction that needs to be read > > for the relation and excludes that the index page fetches. > > That doesn't sound right to me. The genericcostestimate() function > calculates the number of *index* pages fetched. It makes no difference > if it's an Index Scan or an Index Only Scan. > > genericcostestimate() could surely be made smarter. Currently, it > multiplies the number of index pages fetched with random_page_cost, even > though a freshly created index is mostly physically ordered by the keys. > seq_page_cost with some fudge factor might be more appropriate, whether > or not it's an Index Only Scan. Not sure what the exact formula should > be, just replacing random_page_cost with seq_page_cost is surely not > right either. > > - Heikki > So, not actually random replacement here, rather a change with baserel->allvisfrac taken into consideration (as given below): ---- index_random_page_cost = Min(spc_seq_page_cost + spc_random_page_cost * (1.0 - baserel->allvisfrac), spc_random_page_cost); ---- Does this make sense? -- Highgo Software (Canada/China/Pakistan) URL : www.highgo.ca ADDR: 10318 WHALLEY BLVD, Surrey, BC CELL:+923335449950 EMAIL: mailto:[email protected] SKYPE: engineeredvirus ^ permalink raw reply [nested|flat] 1267+ messages in thread
* Re: Improved Cost Calculation for IndexOnlyScan @ 2020-09-29 09:57 Heikki Linnakangas <[email protected]> parent: Hamid Akhtar <[email protected]> 0 siblings, 1 reply; 1267+ messages in thread From: Heikki Linnakangas @ 2020-09-29 09:57 UTC (permalink / raw) To: Hamid Akhtar <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On 29/09/2020 11:49, Hamid Akhtar wrote: > So, not actually random replacement here, rather a change with > baserel->allvisfrac taken into consideration (as given below): > ---- > index_random_page_cost = Min(spc_seq_page_cost + spc_random_page_cost * > (1.0 - baserel->allvisfrac), spc_random_page_cost); > ---- > > Does this make sense? No. genericcostestimate() is only concerned with accesses to the index, not the the heap accesses that are needed with Index Scans. 'allvisfrac' should not affect the number of *index* pages fetched in any way. - Heikki ^ permalink raw reply [nested|flat] 1267+ messages in thread
* Re: Improved Cost Calculation for IndexOnlyScan @ 2020-10-12 10:46 Hamid Akhtar <[email protected]> parent: Heikki Linnakangas <[email protected]> 0 siblings, 1 reply; 1267+ messages in thread From: Hamid Akhtar @ 2020-10-12 10:46 UTC (permalink / raw) To: Heikki Linnakangas <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On Tue, Sep 29, 2020 at 2:57 PM Heikki Linnakangas <[email protected]> wrote: > On 29/09/2020 11:49, Hamid Akhtar wrote: > > So, not actually random replacement here, rather a change with > > baserel->allvisfrac taken into consideration (as given below): > > ---- > > index_random_page_cost = Min(spc_seq_page_cost + spc_random_page_cost * > > (1.0 - baserel->allvisfrac), spc_random_page_cost); > > ---- > > > > Does this make sense? > > No. genericcostestimate() is only concerned with accesses to the index, > not the the heap accesses that are needed with Index Scans. 'allvisfrac' > should not affect the number of *index* pages fetched in any way. > > - Heikki > Currently, the costing for indexonlyscan only differs based on 'allvisfrac'. IIUC, the current implementation changes the number of pages being fetched based on 'allvisfrac'. This patch actually makes indexonlyscan specific changes to genericcostestimate function. Currently, regardless of the value of 'allvisfrac', it is being assumed that the cost of fetching index pages is random page cost. That is not aligned with the current cost calculation for indexonlyscan. Therefore, I'm suggesting to reduce the random page in a similar fashion in case of indexonlyscan. I'm adding this to the commitfest. -- Highgo Software (Canada/China/Pakistan) URL : www.highgo.ca ADDR: 10318 WHALLEY BLVD, Surrey, BC CELL:+923335449950 EMAIL: mailto:[email protected] SKYPE: engineeredvirus ^ permalink raw reply [nested|flat] 1267+ messages in thread
* Re: Improved Cost Calculation for IndexOnlyScan @ 2020-10-13 12:56 Hamid Akhtar <[email protected]> parent: Hamid Akhtar <[email protected]> 0 siblings, 0 replies; 1267+ messages in thread From: Hamid Akhtar @ 2020-10-13 12:56 UTC (permalink / raw) To: Heikki Linnakangas <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On Mon, Oct 12, 2020 at 3:46 PM Hamid Akhtar <[email protected]> wrote: > > > On Tue, Sep 29, 2020 at 2:57 PM Heikki Linnakangas <[email protected]> > wrote: > >> On 29/09/2020 11:49, Hamid Akhtar wrote: >> > So, not actually random replacement here, rather a change with >> > baserel->allvisfrac taken into consideration (as given below): >> > ---- >> > index_random_page_cost = Min(spc_seq_page_cost + spc_random_page_cost * >> > (1.0 - baserel->allvisfrac), spc_random_page_cost); >> > ---- >> > >> > Does this make sense? >> >> No. genericcostestimate() is only concerned with accesses to the index, >> not the the heap accesses that are needed with Index Scans. 'allvisfrac' >> should not affect the number of *index* pages fetched in any way. >> >> - Heikki >> > > Currently, the costing for indexonlyscan only differs based on > 'allvisfrac'. IIUC, the current implementation changes the number of pages > being fetched based on 'allvisfrac'. > > This patch actually makes indexonlyscan specific changes > to genericcostestimate function. Currently, regardless of the value of > 'allvisfrac', it is being assumed that the cost of fetching index pages is > random page cost. That is not aligned with the current cost calculation for > indexonlyscan. Therefore, I'm suggesting to reduce the random page in a > similar fashion in case of indexonlyscan. > > I'm adding this to the commitfest. > Retrospectively looking at the patch, I see your point. Your criticism is valid. I'll revalidate this issue and rework the patch if necessary. > > -- > Highgo Software (Canada/China/Pakistan) > URL : www.highgo.ca > ADDR: 10318 WHALLEY BLVD, Surrey, BC > CELL:+923335449950 EMAIL: mailto:[email protected] > SKYPE: engineeredvirus > -- Highgo Software (Canada/China/Pakistan) URL : www.highgo.ca ADDR: 10318 WHALLEY BLVD, Surrey, BC CELL:+923335449950 EMAIL: mailto:[email protected] SKYPE: engineeredvirus ^ permalink raw reply [nested|flat] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 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] 1267+ 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; 1267+ messages in thread From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw) --- .../sgml/ref/create_materialized_view.sgml | 16 +++++++++-- src/backend/commands/createas.c | 26 +++++++++++------ src/backend/parser/gram.y | 16 +++++++++++ src/include/nodes/primnodes.h | 1 + src/test/regress/expected/matview.out | 28 +++++++++++++++++++ src/test/regress/sql/matview.sql | 15 ++++++++++ 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index b5a8e3441a..65633b8bfa 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ] AS <replaceable>query</replaceable> - [ WITH [ NO ] DATA ] + [ WITH [ NO | OLD ] DATA ] </synopsis> </refsynopsisdiv> @@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam <para> <command>CREATE MATERIALIZED VIEW</command> defines a materialized view of a query. The query is executed and used to populate the view at the time - the command is issued (unless <command>WITH NO DATA</command> is used) and may be + the command is issued (unless <command>WITH NO DATA</command> or + <command>WITH OLD DATA</command> is used) and may be refreshed later using <command>REFRESH MATERIALIZED VIEW</command>. </para> @@ -160,7 +161,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam </varlistentry> <varlistentry> - <term><literal>WITH [ NO ] DATA</literal></term> + <term><literal>WITH [ NO | OLD ] DATA</literal></term> <listitem> <para> This clause specifies whether or not the materialized view should be @@ -168,6 +169,15 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam flagged as unscannable and cannot be queried until <command>REFRESH MATERIALIZED VIEW</command> is used. </para> + + <para> + The form <command>WITH OLD DATA</command> keeps the already stored data + when replacing an existing materialized view to keep it populated. For + newly created materialized views, this has the same effect as + <command>WITH DATA</command>. Use this form if you want to use + <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command> as 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] 1267+ messages in thread
end of thread, other threads:[~2024-07-26 21:33 UTC | newest] Thread overview: 1267+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2020-09-29 07:06 Improved Cost Calculation for IndexOnlyScan Hamid Akhtar <[email protected]> 2020-09-29 08:08 ` Heikki Linnakangas <[email protected]> 2020-09-29 08:49 ` Hamid Akhtar <[email protected]> 2020-09-29 09:57 ` Heikki Linnakangas <[email protected]> 2020-10-12 10:46 ` Hamid Akhtar <[email protected]> 2020-10-13 12:56 ` Hamid Akhtar <[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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox