@@ -81,63 +81,159 @@ static void intorel_destroy(DestReceiver *self);
81
81
static ObjectAddress
82
82
create_ctas_internal (List * attrList , IntoClause * into )
83
83
{
84
- CreateStmt * create = makeNode ( CreateStmt );
85
- bool is_matview ;
84
+ bool is_matview ,
85
+ replace = false ;
86
86
char relkind ;
87
- Datum toast_options ;
88
- const char * const validnsps [] = HEAP_RELOPT_NAMESPACES ;
87
+ Oid matviewOid = InvalidOid ;
89
88
ObjectAddress intoRelationAddr ;
90
89
91
90
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
92
91
is_matview = (into -> viewQuery != NULL );
93
92
relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION ;
94
93
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 ;
109
98
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
+ }
115
104
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 )));
122
119
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);
120
+ CheckTableNotInUse (rel , "CREATE OR REPLACE MATERIALIZED VIEW" );
129
121
130
- (void ) heap_reloptions (RELKIND_TOASTVALUE , toast_options , true);
122
+ descriptor = BuildDescForRelation (attrList );
123
+ checkViewColumns (descriptor , rel -> rd_att , true);
131
124
132
- NewRelationCreateToastTable (intoRelationAddr .objectId , toast_options );
125
+ /* Add new attributes via ALTER TABLE. */
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
+ /* Set access method via ALTER TABLE. */
146
+ if (into -> accessMethod != NULL )
147
+ {
148
+ atcmd = makeNode (AlterTableCmd );
149
+ atcmd -> subtype = AT_SetAccessMethod ;
150
+ atcmd -> name = into -> accessMethod ;
151
+ atcmds = lappend (atcmds , atcmd );
152
+ }
153
+
154
+ /* Set tablespace via ALTER TABLE. */
155
+ if (into -> tableSpaceName != NULL )
156
+ {
157
+ atcmd = makeNode (AlterTableCmd );
158
+ atcmd -> subtype = AT_SetTableSpace ;
159
+ atcmd -> name = into -> tableSpaceName ;
160
+ atcmds = lappend (atcmds , atcmd );
161
+ }
162
+
163
+ /* Set storage parameters via ALTER TABLE. */
164
+ if (into -> options != NIL )
165
+ {
166
+ atcmd = makeNode (AlterTableCmd );
167
+ atcmd -> subtype = AT_ReplaceRelOptions ;
168
+ atcmd -> def = (Node * ) into -> options ;
169
+ atcmds = lappend (atcmds , atcmd );
170
+ }
171
+
172
+ if (atcmds != NIL )
173
+ {
174
+ AlterTableInternal (matviewOid , atcmds , true);
175
+ CommandCounterIncrement ();
176
+ }
177
+
178
+ relation_close (rel , NoLock );
179
+
180
+ ObjectAddressSet (intoRelationAddr , RelationRelationId , matviewOid );
181
+ }
182
+ else
183
+ {
184
+ CreateStmt * create = makeNode (CreateStmt );
185
+ Datum toast_options ;
186
+ const static char * validnsps [] = HEAP_RELOPT_NAMESPACES ;
187
+
188
+ /*
189
+ * Create the target relation by faking up a CREATE TABLE parsetree
190
+ * and passing it to DefineRelation.
191
+ */
192
+ create -> relation = into -> rel ;
193
+ create -> tableElts = attrList ;
194
+ create -> inhRelations = NIL ;
195
+ create -> ofTypename = NULL ;
196
+ create -> constraints = NIL ;
197
+ create -> options = into -> options ;
198
+ create -> oncommit = into -> onCommit ;
199
+ create -> tablespacename = into -> tableSpaceName ;
200
+ create -> if_not_exists = false;
201
+ create -> accessMethod = into -> accessMethod ;
202
+
203
+ /*
204
+ * Create the relation. (This will error out if there's an existing
205
+ * view, so we don't need more code to complain if "replace" is
206
+ * false.)
207
+ */
208
+ intoRelationAddr = DefineRelation (create , relkind , InvalidOid , NULL ,
209
+ NULL );
210
+
211
+ /*
212
+ * If necessary, create a TOAST table for the target table. Note that
213
+ * NewRelationCreateToastTable ends with CommandCounterIncrement(), so
214
+ * that the TOAST table will be visible for insertion.
215
+ */
216
+ CommandCounterIncrement ();
217
+
218
+ /* parse and validate reloptions for the toast table */
219
+ toast_options = transformRelOptions ((Datum ) 0 ,
220
+ create -> options ,
221
+ "toast" ,
222
+ validnsps ,
223
+ true, false);
224
+
225
+ (void ) heap_reloptions (RELKIND_TOASTVALUE , toast_options , true);
226
+
227
+ NewRelationCreateToastTable (intoRelationAddr .objectId , toast_options );
228
+ }
133
229
134
230
/* Create the "view" part of a materialized view. */
135
231
if (is_matview )
136
232
{
137
233
/* StoreViewQuery scribbles on tree, so make a copy */
138
234
Query * query = copyObject (into -> viewQuery );
139
235
140
- StoreViewQuery (intoRelationAddr .objectId , query , false );
236
+ StoreViewQuery (intoRelationAddr .objectId , query , replace );
141
237
CommandCounterIncrement ();
142
238
}
143
239
@@ -234,7 +330,34 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
234
330
235
331
/* Check if the relation exists or not */
236
332
if (CreateTableAsRelExists (stmt ))
333
+ {
334
+ /* An existing materialized view can be replaced. */
335
+ if (is_matview && into -> replace )
336
+ {
337
+ /* Change the relation to match the new query and other options. */
338
+ address = create_ctas_nodata (query -> targetList , into );
339
+
340
+ /*
341
+ * Refresh the materialized view with a fake statement unless we
342
+ * must keep the old data.
343
+ */
344
+ if (!into -> keepData )
345
+ {
346
+ RefreshMatViewStmt * refresh ;
347
+
348
+ refresh = makeNode (RefreshMatViewStmt );
349
+ refresh -> relation = into -> rel ;
350
+ refresh -> skipData = into -> skipData ;
351
+ refresh -> concurrent = false;
352
+
353
+ address = ExecRefreshMatView (refresh , pstate -> p_sourcetext , qc );
354
+ }
355
+
356
+ return address ;
357
+ }
358
+
237
359
return InvalidObjectAddress ;
360
+ }
238
361
239
362
/*
240
363
* Create the tuple receiver object and insert info it will need
@@ -403,26 +526,28 @@ CreateTableAsRelExists(CreateTableAsStmt *ctas)
403
526
oldrelid = get_relname_relid (into -> rel -> relname , nspid );
404
527
if (OidIsValid (oldrelid ))
405
528
{
406
- if (!ctas -> if_not_exists )
529
+ if (!ctas -> if_not_exists && ! into -> replace )
407
530
ereport (ERROR ,
408
531
(errcode (ERRCODE_DUPLICATE_TABLE ),
409
532
errmsg ("relation \"%s\" already exists" ,
410
533
into -> rel -> relname )));
411
534
412
535
/*
413
- * The relation exists and IF NOT EXISTS has been specified.
536
+ * The relation exists and IF NOT EXISTS or OR REPLACE has been
537
+ * specified.
414
538
*
415
539
* If we are in an extension script, insist that the pre-existing
416
540
* object be a member of the extension, to avoid security risks.
417
541
*/
418
542
ObjectAddressSet (address , RelationRelationId , oldrelid );
419
543
checkMembershipInCurrentExtension (& address );
420
544
421
- /* OK to skip */
422
- ereport (NOTICE ,
423
- (errcode (ERRCODE_DUPLICATE_TABLE ),
424
- errmsg ("relation \"%s\" already exists, skipping" ,
425
- into -> rel -> relname )));
545
+ if (ctas -> if_not_exists )
546
+ /* OK to skip */
547
+ ereport (NOTICE ,
548
+ (errcode (ERRCODE_DUPLICATE_TABLE ),
549
+ errmsg ("relation \"%s\" already exists, skipping" ,
550
+ into -> rel -> relname )));
426
551
return true;
427
552
}
428
553
0 commit comments