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