@@ -81,63 +81,154 @@ static void intorel_destroy(DestReceiver *self);
8181static ObjectAddress
8282create_ctas_internal (List * attrList , IntoClause * into )
8383{
84- CreateStmt * create = makeNode ( CreateStmt );
85- bool is_matview ;
84+ bool is_matview ,
85+ replace = false ;
8686 char relkind ;
87- Datum toast_options ;
88- const char * const validnsps [] = HEAP_RELOPT_NAMESPACES ;
87+ Oid matviewOid = InvalidOid ;
8988 ObjectAddress intoRelationAddr ;
9089
9190 /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
9291 is_matview = (into -> viewQuery != NULL );
9392 relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION ;
9493
95- /*
96- * Create the target relation by faking up a CREATE TABLE parsetree and
97- * passing it to DefineRelation.
98- */
99- create -> relation = into -> rel ;
100- create -> tableElts = attrList ;
101- create -> inhRelations = NIL ;
102- create -> ofTypename = NULL ;
103- create -> constraints = NIL ;
104- create -> options = into -> options ;
105- create -> oncommit = into -> onCommit ;
106- create -> tablespacename = into -> tableSpaceName ;
107- create -> if_not_exists = false;
108- create -> accessMethod = into -> accessMethod ;
94+ /* Check if an existing materialized view needs to be replaced. */
95+ if (is_matview )
96+ {
97+ LOCKMODE lockmode ;
10998
110- /*
111- * Create the relation. (This will error out if there's an existing view ,
112- * so we don't need more code to complain if "replace" is false.)
113- */
114- intoRelationAddr = DefineRelation ( create , relkind , InvalidOid , NULL , NULL );
99+ lockmode = into -> replace ? AccessExclusiveLock : NoLock ;
100+ ( void ) RangeVarGetAndCheckCreationNamespace ( into -> rel , lockmode ,
101+ & matviewOid );
102+ replace = OidIsValid ( matviewOid ) && into -> replace ;
103+ }
115104
116- /*
117- * If necessary, create a TOAST table for the target table. Note that
118- * NewRelationCreateToastTable ends with CommandCounterIncrement(), so
119- * that the TOAST table will be visible for insertion.
120- */
121- CommandCounterIncrement ();
105+ if (is_matview && replace )
106+ {
107+ Relation rel ;
108+ List * atcmds = NIL ;
109+ AlterTableCmd * atcmd ;
110+ TupleDesc descriptor ;
111+
112+ rel = relation_open (matviewOid , NoLock );
113+
114+ if (rel -> rd_rel -> relkind != RELKIND_MATVIEW )
115+ ereport (ERROR ,
116+ errcode (ERRCODE_WRONG_OBJECT_TYPE ),
117+ errmsg ("\"%s\" is not a materialized view" ,
118+ RelationGetRelationName (rel )));
119+
120+ CheckTableNotInUse (rel , "CREATE OR REPLACE MATERIALIZED VIEW" );
121+
122+ descriptor = BuildDescForRelation (attrList );
123+ checkViewColumns (descriptor , rel -> rd_att , true);
124+
125+ /* add new attributes */
126+ if (list_length (attrList ) > rel -> rd_att -> natts )
127+ {
128+ ListCell * c ;
129+ int skip = rel -> rd_att -> natts ;
130+
131+ foreach (c , attrList )
132+ {
133+ if (skip > 0 )
134+ {
135+ skip -- ;
136+ continue ;
137+ }
138+ atcmd = makeNode (AlterTableCmd );
139+ atcmd -> subtype = AT_AddColumnToView ;
140+ atcmd -> def = (Node * ) lfirst (c );
141+ atcmds = lappend (atcmds , atcmd );
142+ }
143+ }
144+
145+ /*
146+ * The following alters access method, tablespace, and storage options.
147+ * When replacing an existing matview we need to alter the relation
148+ * such that the defaults apply as if they have not been specified at
149+ * all by the CREATE statement.
150+ */
151+
152+ /* access method */
153+ atcmd = makeNode (AlterTableCmd );
154+ atcmd -> subtype = AT_SetAccessMethod ;
155+ atcmd -> name = into -> accessMethod ? into -> accessMethod : default_table_access_method ;
156+ atcmds = lappend (atcmds , atcmd );
157+
158+ /* tablespace */
159+ atcmd = makeNode (AlterTableCmd );
160+ atcmd -> subtype = AT_SetTableSpace ;
161+ /* use empty string to specify default tablespace */
162+ atcmd -> name = into -> tableSpaceName ? into -> tableSpaceName : "" ;
163+ atcmds = lappend (atcmds , atcmd );
164+
165+ /* storage options */
166+ atcmd = makeNode (AlterTableCmd );
167+ atcmd -> subtype = AT_ReplaceRelOptions ;
168+ atcmd -> def = (Node * ) into -> options ;
169+ atcmds = lappend (atcmds , atcmd );
170+
171+ AlterTableInternal (matviewOid , atcmds , true);
172+ CommandCounterIncrement ();
173+
174+ relation_close (rel , NoLock );
175+ ObjectAddressSet (intoRelationAddr , RelationRelationId , matviewOid );
176+ }
177+ else
178+ {
179+ CreateStmt * create = makeNode (CreateStmt );
180+ Datum toast_options ;
181+ const static char * validnsps [] = HEAP_RELOPT_NAMESPACES ;
122182
123- /* parse and validate reloptions for the toast table */
124- toast_options = transformRelOptions ((Datum ) 0 ,
125- create -> options ,
126- "toast" ,
127- validnsps ,
128- true, false);
183+ /*
184+ * Create the target relation by faking up a CREATE TABLE parsetree
185+ * and passing it to DefineRelation.
186+ */
187+ create -> relation = into -> rel ;
188+ create -> tableElts = attrList ;
189+ create -> inhRelations = NIL ;
190+ create -> ofTypename = NULL ;
191+ create -> constraints = NIL ;
192+ create -> options = into -> options ;
193+ create -> oncommit = into -> onCommit ;
194+ create -> tablespacename = into -> tableSpaceName ;
195+ create -> if_not_exists = false;
196+ create -> accessMethod = into -> accessMethod ;
129197
130- (void ) heap_reloptions (RELKIND_TOASTVALUE , toast_options , true);
198+ /*
199+ * Create the relation. (This will error out if there's an existing
200+ * view, so we don't need more code to complain if "replace" is
201+ * false.)
202+ */
203+ intoRelationAddr = DefineRelation (create , relkind , InvalidOid , NULL ,
204+ NULL );
131205
132- NewRelationCreateToastTable (intoRelationAddr .objectId , toast_options );
206+ /*
207+ * If necessary, create a TOAST table for the target table. Note that
208+ * NewRelationCreateToastTable ends with CommandCounterIncrement(), so
209+ * that the TOAST table will be visible for insertion.
210+ */
211+ CommandCounterIncrement ();
212+
213+ /* parse and validate reloptions for the toast table */
214+ toast_options = transformRelOptions ((Datum ) 0 ,
215+ create -> options ,
216+ "toast" ,
217+ validnsps ,
218+ true, false);
219+
220+ (void ) heap_reloptions (RELKIND_TOASTVALUE , toast_options , true);
221+
222+ NewRelationCreateToastTable (intoRelationAddr .objectId , toast_options );
223+ }
133224
134225 /* Create the "view" part of a materialized view. */
135226 if (is_matview )
136227 {
137228 /* StoreViewQuery scribbles on tree, so make a copy */
138229 Query * query = copyObject (into -> viewQuery );
139230
140- StoreViewQuery (intoRelationAddr .objectId , query , false );
231+ StoreViewQuery (intoRelationAddr .objectId , query , replace );
141232 CommandCounterIncrement ();
142233 }
143234
@@ -234,7 +325,34 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
234325
235326 /* Check if the relation exists or not */
236327 if (CreateTableAsRelExists (stmt ))
328+ {
329+ /* An existing materialized view can be replaced. */
330+ if (is_matview && into -> replace )
331+ {
332+ /* Change the relation to match the new query and other options. */
333+ address = create_ctas_nodata (query -> targetList , into );
334+
335+ /*
336+ * Refresh the materialized view with a fake statement unless we
337+ * must keep the old data.
338+ */
339+ if (!into -> keepData )
340+ {
341+ RefreshMatViewStmt * refresh ;
342+
343+ refresh = makeNode (RefreshMatViewStmt );
344+ refresh -> relation = into -> rel ;
345+ refresh -> skipData = into -> skipData ;
346+ refresh -> concurrent = false;
347+
348+ address = ExecRefreshMatView (refresh , pstate -> p_sourcetext , qc );
349+ }
350+
351+ return address ;
352+ }
353+
237354 return InvalidObjectAddress ;
355+ }
238356
239357 /*
240358 * Create the tuple receiver object and insert info it will need
@@ -273,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
273391 */
274392 if (is_matview )
275393 {
394+ if (into -> keepData )
395+ elog (ERROR , "must not specify WITH OLD DATA when creating a new materialized view" );
396+
276397 do_refresh = !into -> skipData ;
277398 into -> skipData = true;
278399 }
@@ -402,26 +523,28 @@ CreateTableAsRelExists(CreateTableAsStmt *ctas)
402523 oldrelid = get_relname_relid (into -> rel -> relname , nspid );
403524 if (OidIsValid (oldrelid ))
404525 {
405- if (!ctas -> if_not_exists )
526+ if (!ctas -> if_not_exists && ! into -> replace )
406527 ereport (ERROR ,
407528 (errcode (ERRCODE_DUPLICATE_TABLE ),
408529 errmsg ("relation \"%s\" already exists" ,
409530 into -> rel -> relname )));
410531
411532 /*
412- * The relation exists and IF NOT EXISTS has been specified.
533+ * The relation exists and IF NOT EXISTS or OR REPLACE has been
534+ * specified.
413535 *
414536 * If we are in an extension script, insist that the pre-existing
415537 * object be a member of the extension, to avoid security risks.
416538 */
417539 ObjectAddressSet (address , RelationRelationId , oldrelid );
418540 checkMembershipInCurrentExtension (& address );
419541
420- /* OK to skip */
421- ereport (NOTICE ,
422- (errcode (ERRCODE_DUPLICATE_TABLE ),
423- errmsg ("relation \"%s\" already exists, skipping" ,
424- into -> rel -> relname )));
542+ if (ctas -> if_not_exists )
543+ /* OK to skip */
544+ ereport (NOTICE ,
545+ (errcode (ERRCODE_DUPLICATE_TABLE ),
546+ errmsg ("relation \"%s\" already exists, skipping" ,
547+ into -> rel -> relname )));
425548 return true;
426549 }
427550
0 commit comments