Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * createas.c
4 : : * Execution of CREATE TABLE ... AS, a/k/a SELECT INTO.
5 : : * Since CREATE MATERIALIZED VIEW shares syntax and most behaviors,
6 : : * we implement that here, too.
7 : : *
8 : : * We implement this by diverting the query's normal output to a
9 : : * specialized DestReceiver type.
10 : : *
11 : : * Formerly, CTAS was implemented as a variant of SELECT, which led
12 : : * to assorted legacy behaviors that we still try to preserve, notably that
13 : : * we must return a tuples-processed count in the QueryCompletion. (We no
14 : : * longer do that for CTAS ... WITH NO DATA, however.)
15 : : *
16 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
17 : : * Portions Copyright (c) 1994, Regents of the University of California
18 : : *
19 : : *
20 : : * IDENTIFICATION
21 : : * src/backend/commands/createas.c
22 : : *
23 : : *-------------------------------------------------------------------------
24 : : */
25 : : #include "postgres.h"
26 : :
27 : : #include "access/heapam.h"
28 : : #include "access/reloptions.h"
29 : : #include "access/tableam.h"
30 : : #include "access/xact.h"
31 : : #include "catalog/namespace.h"
32 : : #include "catalog/toasting.h"
33 : : #include "commands/createas.h"
34 : : #include "commands/matview.h"
35 : : #include "commands/prepare.h"
36 : : #include "commands/tablecmds.h"
37 : : #include "commands/view.h"
38 : : #include "executor/execdesc.h"
39 : : #include "executor/executor.h"
40 : : #include "nodes/makefuncs.h"
41 : : #include "nodes/nodeFuncs.h"
42 : : #include "nodes/queryjumble.h"
43 : : #include "parser/analyze.h"
44 : : #include "rewrite/rewriteHandler.h"
45 : : #include "tcop/tcopprot.h"
46 : : #include "utils/builtins.h"
47 : : #include "utils/lsyscache.h"
48 : : #include "utils/rls.h"
49 : : #include "utils/snapmgr.h"
50 : :
51 : : typedef struct
52 : : {
53 : : DestReceiver pub; /* publicly-known function pointers */
54 : : IntoClause *into; /* target relation specification */
55 : : /* These fields are filled by intorel_startup: */
56 : : Relation rel; /* relation to write to */
57 : : ObjectAddress reladdr; /* address of rel, for ExecCreateTableAs */
58 : : CommandId output_cid; /* cmin to insert in output tuples */
59 : : int ti_options; /* table_tuple_insert performance options */
60 : : BulkInsertState bistate; /* bulk insert state */
61 : : } DR_intorel;
62 : :
63 : : /* utility functions for CTAS definition creation */
64 : : static ObjectAddress create_ctas_internal(List *attrList, IntoClause *into);
65 : : static ObjectAddress create_ctas_nodata(List *tlist, IntoClause *into);
66 : :
67 : : /* DestReceiver routines for collecting data */
68 : : static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
69 : : static bool intorel_receive(TupleTableSlot *slot, DestReceiver *self);
70 : : static void intorel_shutdown(DestReceiver *self);
71 : : static void intorel_destroy(DestReceiver *self);
72 : :
73 : :
74 : : /*
75 : : * create_ctas_internal
76 : : *
77 : : * Internal utility used for the creation of the definition of a relation
78 : : * created via CREATE TABLE AS or a materialized view. Caller needs to
79 : : * provide a list of attributes (ColumnDef nodes).
80 : : */
81 : : static ObjectAddress
3358 tgl@sss.pgh.pa.us 82 :CBC 859 : create_ctas_internal(List *attrList, IntoClause *into)
83 : : {
84 : 859 : CreateStmt *create = makeNode(CreateStmt);
85 : : bool is_matview;
86 : : char relkind;
87 : : Datum toast_options;
396 heikki.linnakangas@i 88 : 859 : const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
89 : : ObjectAddress intoRelationAddr;
90 : :
91 : : /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
3358 tgl@sss.pgh.pa.us 92 : 859 : is_matview = (into->viewQuery != NULL);
93 [ + + ]: 859 : relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION;
94 : :
95 : : /*
96 : : * Create the target relation by faking up a CREATE TABLE parsetree and
97 : : * passing it to DefineRelation.
98 : : */
99 : 859 : create->relation = into->rel;
100 : 859 : create->tableElts = attrList;
101 : 859 : create->inhRelations = NIL;
102 : 859 : create->ofTypename = NULL;
103 : 859 : create->constraints = NIL;
104 : 859 : create->options = into->options;
105 : 859 : create->oncommit = into->onCommit;
106 : 859 : create->tablespacename = into->tableSpaceName;
107 : 859 : create->if_not_exists = false;
2376 andres@anarazel.de 108 : 859 : create->accessMethod = into->accessMethod;
109 : :
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 : : */
3195 rhaas@postgresql.org 114 : 859 : intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
115 : :
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 : : */
3358 tgl@sss.pgh.pa.us 121 : 850 : CommandCounterIncrement();
122 : :
123 : : /* parse and validate reloptions for the toast table */
124 : 850 : toast_options = transformRelOptions((Datum) 0,
125 : : create->options,
126 : : "toast",
127 : : validnsps,
128 : : true, false);
129 : :
513 akorotkov@postgresql 130 : 850 : (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
131 : :
3358 tgl@sss.pgh.pa.us 132 : 850 : NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
133 : :
134 : : /* Create the "view" part of a materialized view. */
135 [ + + ]: 850 : if (is_matview)
136 : : {
137 : : /* StoreViewQuery scribbles on tree, so make a copy */
324 peter@eisentraut.org 138 : 231 : Query *query = copyObject(into->viewQuery);
139 : :
3358 tgl@sss.pgh.pa.us 140 : 231 : StoreViewQuery(intoRelationAddr.objectId, query, false);
141 : 228 : CommandCounterIncrement();
142 : : }
143 : :
144 : 847 : return intoRelationAddr;
145 : : }
146 : :
147 : :
148 : : /*
149 : : * create_ctas_nodata
150 : : *
151 : : * Create CTAS or materialized view when WITH NO DATA is used, starting from
152 : : * the targetlist of the SELECT or view definition.
153 : : */
154 : : static ObjectAddress
155 : 244 : create_ctas_nodata(List *tlist, IntoClause *into)
156 : : {
157 : : List *attrList;
158 : : ListCell *t,
159 : : *lc;
160 : :
161 : : /*
162 : : * Build list of ColumnDefs from non-junk elements of the tlist. If a
163 : : * column name list was specified in CREATE TABLE AS, override the column
164 : : * names in the query. (Too few column names are OK, too many are not.)
165 : : */
166 : 244 : attrList = NIL;
167 : 244 : lc = list_head(into->colNames);
168 [ + - + + : 773 : foreach(t, tlist)
+ + ]
169 : : {
170 : 529 : TargetEntry *tle = (TargetEntry *) lfirst(t);
171 : :
172 [ + - ]: 529 : if (!tle->resjunk)
173 : : {
174 : : ColumnDef *col;
175 : : char *colname;
176 : :
177 [ + + ]: 529 : if (lc)
178 : : {
179 : 62 : colname = strVal(lfirst(lc));
2245 180 : 62 : lc = lnext(into->colNames, lc);
181 : : }
182 : : else
3358 183 : 467 : colname = tle->resname;
184 : :
185 : 529 : col = makeColumnDef(colname,
186 : 529 : exprType((Node *) tle->expr),
187 : 529 : exprTypmod((Node *) tle->expr),
188 : 529 : exprCollation((Node *) tle->expr));
189 : :
190 : : /*
191 : : * It's possible that the column is of a collatable type but the
192 : : * collation could not be resolved, so double-check. (We must
193 : : * check this here because DefineRelation would adopt the type's
194 : : * default collation rather than complaining.)
195 : : */
196 [ + + - + ]: 966 : if (!OidIsValid(col->collOid) &&
197 : 437 : type_is_collatable(col->typeName->typeOid))
3358 tgl@sss.pgh.pa.us 198 [ # # ]:UBC 0 : ereport(ERROR,
199 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
200 : : errmsg("no collation was derived for column \"%s\" with collatable type %s",
201 : : col->colname,
202 : : format_type_be(col->typeName->typeOid)),
203 : : errhint("Use the COLLATE clause to set the collation explicitly.")));
204 : :
3358 tgl@sss.pgh.pa.us 205 :CBC 529 : attrList = lappend(attrList, col);
206 : : }
207 : : }
208 : :
209 [ + + ]: 244 : if (lc != NULL)
210 [ + - ]: 9 : ereport(ERROR,
211 : : (errcode(ERRCODE_SYNTAX_ERROR),
212 : : errmsg("too many column names were specified")));
213 : :
214 : : /* Create the relation definition using the ColumnDef list */
215 : 235 : return create_ctas_internal(attrList, into);
216 : : }
217 : :
218 : :
219 : : /*
220 : : * ExecCreateTableAs -- execute a CREATE TABLE AS command
221 : : */
222 : : ObjectAddress
2072 peter@eisentraut.org 223 : 896 : ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
224 : : ParamListInfo params, QueryEnvironment *queryEnv,
225 : : QueryCompletion *qc)
226 : : {
3145 andres@anarazel.de 227 : 896 : Query *query = castNode(Query, stmt->query);
4919 tgl@sss.pgh.pa.us 228 : 896 : IntoClause *into = stmt->into;
313 michael@paquier.xyz 229 : 896 : JumbleState *jstate = NULL;
4439 noah@leadboat.com 230 : 896 : bool is_matview = (into->viewQuery != NULL);
417 jdavis@postgresql.or 231 : 896 : bool do_refresh = false;
232 : : DestReceiver *dest;
233 : : ObjectAddress address;
234 : :
235 : : /* Check if the relation exists or not */
1711 michael@paquier.xyz 236 [ + + ]: 896 : if (CreateTableAsRelExists(stmt))
237 : 23 : return InvalidObjectAddress;
238 : :
239 : : /*
240 : : * Create the tuple receiver object and insert info it will need
241 : : */
4919 tgl@sss.pgh.pa.us 242 : 850 : dest = CreateIntoRelDestReceiver(into);
243 : :
244 : : /* Query contained by CTAS needs to be jumbled if requested */
313 michael@paquier.xyz 245 [ + + ]: 850 : if (IsQueryIdEnabled())
246 : 220 : jstate = JumbleQuery(query);
247 : :
248 [ + + ]: 850 : if (post_parse_analyze_hook)
249 : 220 : (*post_parse_analyze_hook) (pstate, query, jstate);
250 : :
251 : : /*
252 : : * The contained Query could be a SELECT, or an EXECUTE utility command.
253 : : * If the latter, we just pass it off to ExecuteQuery.
254 : : */
4919 tgl@sss.pgh.pa.us 255 [ + + ]: 850 : if (query->commandType == CMD_UTILITY &&
256 [ + - ]: 23 : IsA(query->utilityStmt, ExecuteStmt))
257 : : {
3145 andres@anarazel.de 258 : 23 : ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
259 : :
4439 noah@leadboat.com 260 [ - + ]: 23 : Assert(!is_matview); /* excluded by syntax */
2014 alvherre@alvh.no-ip. 261 : 23 : ExecuteQuery(pstate, estmt, into, params, dest, qc);
262 : :
263 : : /* get object address that intorel_startup saved for us */
3358 tgl@sss.pgh.pa.us 264 : 23 : address = ((DR_intorel *) dest)->reladdr;
265 : :
3840 alvherre@alvh.no-ip. 266 : 23 : return address;
267 : : }
4530 tgl@sss.pgh.pa.us 268 [ - + ]: 827 : Assert(query->commandType == CMD_SELECT);
269 : :
270 : : /*
271 : : * For materialized views, always skip data during table creation, and use
272 : : * REFRESH instead (see below).
273 : : */
4439 noah@leadboat.com 274 [ + + ]: 827 : if (is_matview)
275 : : {
417 jdavis@postgresql.or 276 : 231 : do_refresh = !into->skipData;
277 : 231 : into->skipData = true;
278 : : }
279 : :
3358 tgl@sss.pgh.pa.us 280 [ + + ]: 827 : if (into->skipData)
281 : : {
282 : : /*
283 : : * If WITH NO DATA was specified, do not go through the rewriter,
284 : : * planner and executor. Just define the relation using a code path
285 : : * similar to CREATE VIEW. This avoids dump/restore problems stemming
286 : : * from running the planner before all dependencies are set up.
287 : : */
288 : 244 : address = create_ctas_nodata(query->targetList, into);
289 : :
290 : : /*
291 : : * For materialized views, reuse the REFRESH logic, which locks down
292 : : * security-restricted operations and restricts the search_path. This
293 : : * reduces the chance that a subsequent refresh will fail.
294 : : */
400 jdavis@postgresql.or 295 [ + + ]: 232 : if (do_refresh)
296 : 181 : RefreshMatViewByOid(address.objectId, true, false, false,
297 : : pstate->p_sourcetext, qc);
298 : :
299 : : }
300 : : else
301 : : {
302 : : List *rewritten;
303 : : PlannedStmt *plan;
304 : : QueryDesc *queryDesc;
305 : :
306 [ - + ]: 583 : Assert(!is_matview);
307 : :
308 : : /*
309 : : * Parse analysis was done already, but we still have to run the rule
310 : : * rewriter. We do not do AcquireRewriteLocks: we assume the query
311 : : * either came straight from the parser, or suitable locks were
312 : : * acquired by plancache.c.
313 : : */
1541 tgl@sss.pgh.pa.us 314 : 583 : rewritten = QueryRewrite(query);
315 : :
316 : : /* SELECT should never rewrite to more or less than one SELECT query */
3358 317 [ - + ]: 583 : if (list_length(rewritten) != 1)
400 jdavis@postgresql.or 318 [ # # ]:UBC 0 : elog(ERROR, "unexpected rewrite result for CREATE TABLE AS SELECT");
3071 tgl@sss.pgh.pa.us 319 :CBC 583 : query = linitial_node(Query, rewritten);
3358 320 [ - + ]: 583 : Assert(query->commandType == CMD_SELECT);
321 : :
322 : : /* plan the query */
1986 fujii@postgresql.org 323 : 583 : plan = pg_plan_query(query, pstate->p_sourcetext,
324 : : CURSOR_OPT_PARALLEL_OK, params);
325 : :
326 : : /*
327 : : * Use a snapshot with an updated command ID to ensure this query sees
328 : : * results of any previously executed queries. (This could only
329 : : * matter if the planner executed an allegedly-stable function that
330 : : * changed the database contents, but let's do it anyway to be
331 : : * parallel to the EXPLAIN code path.)
332 : : */
3358 tgl@sss.pgh.pa.us 333 : 583 : PushCopiedSnapshot(GetActiveSnapshot());
334 : 583 : UpdateActiveSnapshotCommandId();
335 : :
336 : : /* Create a QueryDesc, redirecting output to our tuple receiver */
107 amitlan@postgresql.o 337 : 583 : queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
338 : : GetActiveSnapshot(), InvalidSnapshot,
339 : : dest, params, queryEnv, 0);
340 : :
341 : : /* call ExecutorStart to prepare the plan for execution */
342 : 583 : ExecutorStart(queryDesc, GetIntoRelEFlags(into));
343 : :
344 : : /* run the plan to completion */
271 tgl@sss.pgh.pa.us 345 : 583 : ExecutorRun(queryDesc, ForwardScanDirection, 0);
346 : :
347 : : /* save the rowcount if we're given a qc to fill */
2014 alvherre@alvh.no-ip. 348 [ + + ]: 565 : if (qc)
349 : 561 : SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
350 : :
351 : : /* get object address that intorel_startup saved for us */
3358 tgl@sss.pgh.pa.us 352 : 565 : address = ((DR_intorel *) dest)->reladdr;
353 : :
354 : : /* and clean up */
355 : 565 : ExecutorFinish(queryDesc);
356 : 565 : ExecutorEnd(queryDesc);
357 : :
358 : 565 : FreeQueryDesc(queryDesc);
359 : :
360 : 565 : PopActiveSnapshot();
361 : : }
362 : :
3840 alvherre@alvh.no-ip. 363 : 794 : return address;
364 : : }
365 : :
366 : : /*
367 : : * GetIntoRelEFlags --- compute executor flags needed for CREATE TABLE AS
368 : : *
369 : : * This is exported because EXPLAIN and PREPARE need it too. (Note: those
370 : : * callers still need to deal explicitly with the skipData flag; since they
371 : : * use different methods for suppressing execution, it doesn't seem worth
372 : : * trying to encapsulate that part.)
373 : : */
374 : : int
4919 tgl@sss.pgh.pa.us 375 : 659 : GetIntoRelEFlags(IntoClause *intoClause)
376 : : {
2482 andres@anarazel.de 377 : 659 : int flags = 0;
378 : :
4570 kgrittn@postgresql.o 379 [ + + ]: 659 : if (intoClause->skipData)
380 : 21 : flags |= EXEC_FLAG_WITH_NO_DATA;
381 : :
382 : 659 : return flags;
383 : : }
384 : :
385 : : /*
386 : : * CreateTableAsRelExists --- check existence of relation for CreateTableAsStmt
387 : : *
388 : : * Utility wrapper checking if the relation pending for creation in this
389 : : * CreateTableAsStmt query already exists or not. Returns true if the
390 : : * relation exists, otherwise false.
391 : : */
392 : : bool
1711 michael@paquier.xyz 393 : 979 : CreateTableAsRelExists(CreateTableAsStmt *ctas)
394 : : {
395 : : Oid nspid;
396 : : Oid oldrelid;
397 : : ObjectAddress address;
398 : 979 : IntoClause *into = ctas->into;
399 : :
400 : 979 : nspid = RangeVarGetCreationNamespace(into->rel);
401 : :
1125 tgl@sss.pgh.pa.us 402 : 979 : oldrelid = get_relname_relid(into->rel->relname, nspid);
403 [ + + ]: 979 : if (OidIsValid(oldrelid))
404 : : {
1711 michael@paquier.xyz 405 [ + + ]: 76 : if (!ctas->if_not_exists)
406 [ + - ]: 36 : ereport(ERROR,
407 : : (errcode(ERRCODE_DUPLICATE_TABLE),
408 : : errmsg("relation \"%s\" already exists",
409 : : into->rel->relname)));
410 : :
411 : : /*
412 : : * The relation exists and IF NOT EXISTS has been specified.
413 : : *
414 : : * If we are in an extension script, insist that the pre-existing
415 : : * object be a member of the extension, to avoid security risks.
416 : : */
1125 tgl@sss.pgh.pa.us 417 : 40 : ObjectAddressSet(address, RelationRelationId, oldrelid);
418 : 40 : checkMembershipInCurrentExtension(&address);
419 : :
420 : : /* OK to skip */
1711 michael@paquier.xyz 421 [ + + ]: 38 : ereport(NOTICE,
422 : : (errcode(ERRCODE_DUPLICATE_TABLE),
423 : : errmsg("relation \"%s\" already exists, skipping",
424 : : into->rel->relname)));
425 : 38 : return true;
426 : : }
427 : :
428 : : /* Relation does not exist, it can be created */
429 : 903 : return false;
430 : : }
431 : :
432 : : /*
433 : : * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
434 : : *
435 : : * intoClause will be NULL if called from CreateDestReceiver(), in which
436 : : * case it has to be provided later. However, it is convenient to allow
437 : : * self->into to be filled in immediately for other callers.
438 : : */
439 : : DestReceiver *
4919 tgl@sss.pgh.pa.us 440 : 903 : CreateIntoRelDestReceiver(IntoClause *intoClause)
441 : : {
442 : 903 : DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
443 : :
444 : 903 : self->pub.receiveSlot = intorel_receive;
445 : 903 : self->pub.rStartup = intorel_startup;
446 : 903 : self->pub.rShutdown = intorel_shutdown;
447 : 903 : self->pub.rDestroy = intorel_destroy;
448 : 903 : self->pub.mydest = DestIntoRel;
449 : 903 : self->into = intoClause;
450 : : /* other private fields will be set during intorel_startup */
451 : :
452 : 903 : return (DestReceiver *) self;
453 : : }
454 : :
455 : : /*
456 : : * intorel_startup --- executor startup
457 : : */
458 : : static void
459 : 633 : intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
460 : : {
461 : 633 : DR_intorel *myState = (DR_intorel *) self;
462 : 633 : IntoClause *into = myState->into;
463 : : bool is_matview;
464 : : List *attrList;
465 : : ObjectAddress intoRelationAddr;
466 : : Relation intoRelationDesc;
467 : : ListCell *lc;
468 : : int attnum;
469 : :
470 [ - + ]: 633 : Assert(into != NULL); /* else somebody forgot to set it */
471 : :
472 : : /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
4530 473 : 633 : is_matview = (into->viewQuery != NULL);
474 : :
475 : : /*
476 : : * Build column definitions using "pre-cooked" type and collation info. If
477 : : * a column name list was specified in CREATE TABLE AS, override the
478 : : * column names derived from the query. (Too few column names are OK, too
479 : : * many are not.)
480 : : */
3358 481 : 633 : attrList = NIL;
4919 482 : 633 : lc = list_head(into->colNames);
483 [ + + ]: 2517 : for (attnum = 0; attnum < typeinfo->natts; attnum++)
484 : : {
2939 andres@anarazel.de 485 : 1890 : Form_pg_attribute attribute = TupleDescAttr(typeinfo, attnum);
486 : : ColumnDef *col;
487 : : char *colname;
488 : :
4919 tgl@sss.pgh.pa.us 489 [ + + ]: 1890 : if (lc)
490 : : {
3358 491 : 109 : colname = strVal(lfirst(lc));
2245 492 : 109 : lc = lnext(into->colNames, lc);
493 : : }
494 : : else
3358 495 : 1781 : colname = NameStr(attribute->attname);
496 : :
497 : 1890 : col = makeColumnDef(colname,
498 : : attribute->atttypid,
499 : : attribute->atttypmod,
500 : : attribute->attcollation);
501 : :
502 : : /*
503 : : * It's possible that the column is of a collatable type but the
504 : : * collation could not be resolved, so double-check. (We must check
505 : : * this here because DefineRelation would adopt the type's default
506 : : * collation rather than complaining.)
507 : : */
4919 508 [ + + + + ]: 3510 : if (!OidIsValid(col->collOid) &&
3358 509 : 1620 : type_is_collatable(col->typeName->typeOid))
4919 510 [ + - ]: 6 : ereport(ERROR,
511 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
512 : : errmsg("no collation was derived for column \"%s\" with collatable type %s",
513 : : col->colname,
514 : : format_type_be(col->typeName->typeOid)),
515 : : errhint("Use the COLLATE clause to set the collation explicitly.")));
516 : :
3358 517 : 1884 : attrList = lappend(attrList, col);
518 : : }
519 : :
4919 520 [ + + ]: 627 : if (lc != NULL)
521 [ + - ]: 3 : ereport(ERROR,
522 : : (errcode(ERRCODE_SYNTAX_ERROR),
523 : : errmsg("too many column names were specified")));
524 : :
525 : : /*
526 : : * Actually create the target table
527 : : */
3358 528 : 624 : intoRelationAddr = create_ctas_internal(attrList, into);
529 : :
530 : : /*
531 : : * Finally we can open the target table
532 : : */
2420 andres@anarazel.de 533 : 615 : intoRelationDesc = table_open(intoRelationAddr.objectId, AccessExclusiveLock);
534 : :
535 : : /*
536 : : * Make sure the constructed table does not have RLS enabled.
537 : : *
538 : : * check_enable_rls() will ereport(ERROR) itself if the user has requested
539 : : * something invalid, and otherwise will return RLS_ENABLED if RLS should
540 : : * be enabled here. We don't actually support that currently, so throw
541 : : * our own ereport(ERROR) if that happens.
542 : : */
3840 alvherre@alvh.no-ip. 543 [ - + ]: 615 : if (check_enable_rls(intoRelationAddr.objectId, InvalidOid, false) == RLS_ENABLED)
4005 sfrost@snowman.net 544 [ # # ]:UBC 0 : ereport(ERROR,
545 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
546 : : errmsg("policies not yet implemented for this command")));
547 : :
548 : : /*
549 : : * Tentatively mark the target as populated, if it's a matview and we're
550 : : * going to fill it; otherwise, no change needed.
551 : : */
4506 tgl@sss.pgh.pa.us 552 [ + + + + ]:CBC 615 : if (is_matview && !into->skipData)
553 : 3 : SetMatViewPopulatedState(intoRelationDesc, true);
554 : :
555 : : /*
556 : : * Fill private fields of myState for use by later routines
557 : : */
4919 558 : 615 : myState->rel = intoRelationDesc;
3358 559 : 615 : myState->reladdr = intoRelationAddr;
4919 560 : 615 : myState->output_cid = GetCurrentCommandId(true);
1981 noah@leadboat.com 561 : 615 : myState->ti_options = TABLE_INSERT_SKIP_FSM;
562 : :
563 : : /*
564 : : * If WITH NO DATA is specified, there is no need to set up the state for
565 : : * bulk inserts as there are no tuples to insert.
566 : : */
1755 michael@paquier.xyz 567 [ + + ]: 615 : if (!into->skipData)
568 : 597 : myState->bistate = GetBulkInsertState();
569 : : else
570 : 18 : myState->bistate = NULL;
571 : :
572 : : /*
573 : : * Valid smgr_targblock implies something already wrote to the relation.
574 : : * This may be harmless, but this function hasn't planned for it.
575 : : */
4919 tgl@sss.pgh.pa.us 576 [ - + - - ]: 615 : Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
577 : 615 : }
578 : :
579 : : /*
580 : : * intorel_receive --- receive one tuple
581 : : */
582 : : static bool
583 : 1130971 : intorel_receive(TupleTableSlot *slot, DestReceiver *self)
584 : : {
585 : 1130971 : DR_intorel *myState = (DR_intorel *) self;
586 : :
587 : : /* Nothing to insert if WITH NO DATA is specified. */
1755 michael@paquier.xyz 588 [ + - ]: 1130971 : if (!myState->into->skipData)
589 : : {
590 : : /*
591 : : * Note that the input slot might not be of the type of the target
592 : : * relation. That's supported by table_tuple_insert(), but slightly
593 : : * less efficient than inserting with the right slot - but the
594 : : * alternative would be to copy into a slot of the right type, which
595 : : * would not be cheap either. This also doesn't allow accessing per-AM
596 : : * data (say a tuple's xmin), but since we don't do that here...
597 : : */
598 : 1130971 : table_tuple_insert(myState->rel,
599 : : slot,
600 : : myState->output_cid,
601 : : myState->ti_options,
602 : : myState->bistate);
603 : : }
604 : :
605 : : /* We know this is a newly created relation, so there are no indexes */
606 : :
3379 rhaas@postgresql.org 607 : 1130971 : return true;
608 : : }
609 : :
610 : : /*
611 : : * intorel_shutdown --- executor end
612 : : */
613 : : static void
4919 tgl@sss.pgh.pa.us 614 : 615 : intorel_shutdown(DestReceiver *self)
615 : : {
616 : 615 : DR_intorel *myState = (DR_intorel *) self;
1755 michael@paquier.xyz 617 : 615 : IntoClause *into = myState->into;
618 : :
619 [ + + ]: 615 : if (!into->skipData)
620 : : {
621 : 597 : FreeBulkInsertState(myState->bistate);
622 : 597 : table_finish_bulk_insert(myState->rel, myState->ti_options);
623 : : }
624 : :
625 : : /* close rel, but keep lock until commit */
2420 andres@anarazel.de 626 : 615 : table_close(myState->rel, NoLock);
4919 tgl@sss.pgh.pa.us 627 : 615 : myState->rel = NULL;
628 : 615 : }
629 : :
630 : : /*
631 : : * intorel_destroy --- release DestReceiver object
632 : : */
633 : : static void
634 : 53 : intorel_destroy(DestReceiver *self)
635 : : {
636 : 53 : pfree(self);
637 : 53 : }
|