Move routines to manipulate WAL into PostgreSQL::Test::Cluster
[pgsql.git] / src / backend / commands / createas.c
blob23cecd99c9ecc33814dfd07384ae0b78d1860a9a
1 /*-------------------------------------------------------------------------
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.
8 * We implement this by diverting the query's normal output to a
9 * specialized DestReceiver type.
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.)
16 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
17 * Portions Copyright (c) 1994, Regents of the University of California
20 * IDENTIFICATION
21 * src/backend/commands/createas.c
23 *-------------------------------------------------------------------------
25 #include "postgres.h"
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 "nodes/makefuncs.h"
39 #include "nodes/nodeFuncs.h"
40 #include "nodes/queryjumble.h"
41 #include "parser/analyze.h"
42 #include "rewrite/rewriteHandler.h"
43 #include "tcop/tcopprot.h"
44 #include "utils/builtins.h"
45 #include "utils/lsyscache.h"
46 #include "utils/rls.h"
47 #include "utils/snapmgr.h"
49 typedef struct
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;
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);
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);
73 * create_ctas_internal
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).
79 static ObjectAddress
80 create_ctas_internal(List *attrList, IntoClause *into)
82 CreateStmt *create = makeNode(CreateStmt);
83 bool is_matview;
84 char relkind;
85 Datum toast_options;
86 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
87 ObjectAddress intoRelationAddr;
89 /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
90 is_matview = (into->viewQuery != NULL);
91 relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION;
94 * Create the target relation by faking up a CREATE TABLE parsetree and
95 * passing it to DefineRelation.
97 create->relation = into->rel;
98 create->tableElts = attrList;
99 create->inhRelations = NIL;
100 create->ofTypename = NULL;
101 create->constraints = NIL;
102 create->options = into->options;
103 create->oncommit = into->onCommit;
104 create->tablespacename = into->tableSpaceName;
105 create->if_not_exists = false;
106 create->accessMethod = into->accessMethod;
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.)
112 intoRelationAddr = DefineRelation(create, relkind, InvalidOid, NULL, NULL);
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.
119 CommandCounterIncrement();
121 /* parse and validate reloptions for the toast table */
122 toast_options = transformRelOptions((Datum) 0,
123 create->options,
124 "toast",
125 validnsps,
126 true, false);
128 (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
130 NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
132 /* Create the "view" part of a materialized view. */
133 if (is_matview)
135 /* StoreViewQuery scribbles on tree, so make a copy */
136 Query *query = copyObject(into->viewQuery);
138 StoreViewQuery(intoRelationAddr.objectId, query, false);
139 CommandCounterIncrement();
142 return intoRelationAddr;
147 * create_ctas_nodata
149 * Create CTAS or materialized view when WITH NO DATA is used, starting from
150 * the targetlist of the SELECT or view definition.
152 static ObjectAddress
153 create_ctas_nodata(List *tlist, IntoClause *into)
155 List *attrList;
156 ListCell *t,
157 *lc;
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.)
164 attrList = NIL;
165 lc = list_head(into->colNames);
166 foreach(t, tlist)
168 TargetEntry *tle = (TargetEntry *) lfirst(t);
170 if (!tle->resjunk)
172 ColumnDef *col;
173 char *colname;
175 if (lc)
177 colname = strVal(lfirst(lc));
178 lc = lnext(into->colNames, lc);
180 else
181 colname = tle->resname;
183 col = makeColumnDef(colname,
184 exprType((Node *) tle->expr),
185 exprTypmod((Node *) tle->expr),
186 exprCollation((Node *) tle->expr));
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.)
194 if (!OidIsValid(col->collOid) &&
195 type_is_collatable(col->typeName->typeOid))
196 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.")));
203 attrList = lappend(attrList, col);
207 if (lc != NULL)
208 ereport(ERROR,
209 (errcode(ERRCODE_SYNTAX_ERROR),
210 errmsg("too many column names were specified")));
212 /* Create the relation definition using the ColumnDef list */
213 return create_ctas_internal(attrList, into);
218 * ExecCreateTableAs -- execute a CREATE TABLE AS command
220 ObjectAddress
221 ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
222 ParamListInfo params, QueryEnvironment *queryEnv,
223 QueryCompletion *qc)
225 Query *query = castNode(Query, stmt->query);
226 IntoClause *into = stmt->into;
227 JumbleState *jstate = NULL;
228 bool is_matview = (into->viewQuery != NULL);
229 bool do_refresh = false;
230 DestReceiver *dest;
231 ObjectAddress address;
233 /* Check if the relation exists or not */
234 if (CreateTableAsRelExists(stmt))
235 return InvalidObjectAddress;
238 * Create the tuple receiver object and insert info it will need
240 dest = CreateIntoRelDestReceiver(into);
242 /* Query contained by CTAS needs to be jumbled if requested */
243 if (IsQueryIdEnabled())
244 jstate = JumbleQuery(query);
246 if (post_parse_analyze_hook)
247 (*post_parse_analyze_hook) (pstate, query, jstate);
250 * The contained Query could be a SELECT, or an EXECUTE utility command.
251 * If the latter, we just pass it off to ExecuteQuery.
253 if (query->commandType == CMD_UTILITY &&
254 IsA(query->utilityStmt, ExecuteStmt))
256 ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
258 Assert(!is_matview); /* excluded by syntax */
259 ExecuteQuery(pstate, estmt, into, params, dest, qc);
261 /* get object address that intorel_startup saved for us */
262 address = ((DR_intorel *) dest)->reladdr;
264 return address;
266 Assert(query->commandType == CMD_SELECT);
269 * For materialized views, always skip data during table creation, and use
270 * REFRESH instead (see below).
272 if (is_matview)
274 do_refresh = !into->skipData;
275 into->skipData = true;
278 if (into->skipData)
281 * If WITH NO DATA was specified, do not go through the rewriter,
282 * planner and executor. Just define the relation using a code path
283 * similar to CREATE VIEW. This avoids dump/restore problems stemming
284 * from running the planner before all dependencies are set up.
286 address = create_ctas_nodata(query->targetList, into);
289 * For materialized views, reuse the REFRESH logic, which locks down
290 * security-restricted operations and restricts the search_path. This
291 * reduces the chance that a subsequent refresh will fail.
293 if (do_refresh)
294 RefreshMatViewByOid(address.objectId, true, false, false,
295 pstate->p_sourcetext, qc);
298 else
300 List *rewritten;
301 PlannedStmt *plan;
302 QueryDesc *queryDesc;
304 Assert(!is_matview);
307 * Parse analysis was done already, but we still have to run the rule
308 * rewriter. We do not do AcquireRewriteLocks: we assume the query
309 * either came straight from the parser, or suitable locks were
310 * acquired by plancache.c.
312 rewritten = QueryRewrite(query);
314 /* SELECT should never rewrite to more or less than one SELECT query */
315 if (list_length(rewritten) != 1)
316 elog(ERROR, "unexpected rewrite result for CREATE TABLE AS SELECT");
317 query = linitial_node(Query, rewritten);
318 Assert(query->commandType == CMD_SELECT);
320 /* plan the query */
321 plan = pg_plan_query(query, pstate->p_sourcetext,
322 CURSOR_OPT_PARALLEL_OK, params);
325 * Use a snapshot with an updated command ID to ensure this query sees
326 * results of any previously executed queries. (This could only
327 * matter if the planner executed an allegedly-stable function that
328 * changed the database contents, but let's do it anyway to be
329 * parallel to the EXPLAIN code path.)
331 PushCopiedSnapshot(GetActiveSnapshot());
332 UpdateActiveSnapshotCommandId();
334 /* Create a QueryDesc, redirecting output to our tuple receiver */
335 queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
336 GetActiveSnapshot(), InvalidSnapshot,
337 dest, params, queryEnv, 0);
339 /* call ExecutorStart to prepare the plan for execution */
340 ExecutorStart(queryDesc, GetIntoRelEFlags(into));
342 /* run the plan to completion */
343 ExecutorRun(queryDesc, ForwardScanDirection, 0);
345 /* save the rowcount if we're given a qc to fill */
346 if (qc)
347 SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
349 /* get object address that intorel_startup saved for us */
350 address = ((DR_intorel *) dest)->reladdr;
352 /* and clean up */
353 ExecutorFinish(queryDesc);
354 ExecutorEnd(queryDesc);
356 FreeQueryDesc(queryDesc);
358 PopActiveSnapshot();
361 return address;
365 * GetIntoRelEFlags --- compute executor flags needed for CREATE TABLE AS
367 * This is exported because EXPLAIN and PREPARE need it too. (Note: those
368 * callers still need to deal explicitly with the skipData flag; since they
369 * use different methods for suppressing execution, it doesn't seem worth
370 * trying to encapsulate that part.)
373 GetIntoRelEFlags(IntoClause *intoClause)
375 int flags = 0;
377 if (intoClause->skipData)
378 flags |= EXEC_FLAG_WITH_NO_DATA;
380 return flags;
384 * CreateTableAsRelExists --- check existence of relation for CreateTableAsStmt
386 * Utility wrapper checking if the relation pending for creation in this
387 * CreateTableAsStmt query already exists or not. Returns true if the
388 * relation exists, otherwise false.
390 bool
391 CreateTableAsRelExists(CreateTableAsStmt *ctas)
393 Oid nspid;
394 Oid oldrelid;
395 ObjectAddress address;
396 IntoClause *into = ctas->into;
398 nspid = RangeVarGetCreationNamespace(into->rel);
400 oldrelid = get_relname_relid(into->rel->relname, nspid);
401 if (OidIsValid(oldrelid))
403 if (!ctas->if_not_exists)
404 ereport(ERROR,
405 (errcode(ERRCODE_DUPLICATE_TABLE),
406 errmsg("relation \"%s\" already exists",
407 into->rel->relname)));
410 * The relation exists and IF NOT EXISTS has been specified.
412 * If we are in an extension script, insist that the pre-existing
413 * object be a member of the extension, to avoid security risks.
415 ObjectAddressSet(address, RelationRelationId, oldrelid);
416 checkMembershipInCurrentExtension(&address);
418 /* OK to skip */
419 ereport(NOTICE,
420 (errcode(ERRCODE_DUPLICATE_TABLE),
421 errmsg("relation \"%s\" already exists, skipping",
422 into->rel->relname)));
423 return true;
426 /* Relation does not exist, it can be created */
427 return false;
431 * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
433 * intoClause will be NULL if called from CreateDestReceiver(), in which
434 * case it has to be provided later. However, it is convenient to allow
435 * self->into to be filled in immediately for other callers.
437 DestReceiver *
438 CreateIntoRelDestReceiver(IntoClause *intoClause)
440 DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
442 self->pub.receiveSlot = intorel_receive;
443 self->pub.rStartup = intorel_startup;
444 self->pub.rShutdown = intorel_shutdown;
445 self->pub.rDestroy = intorel_destroy;
446 self->pub.mydest = DestIntoRel;
447 self->into = intoClause;
448 /* other private fields will be set during intorel_startup */
450 return (DestReceiver *) self;
454 * intorel_startup --- executor startup
456 static void
457 intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
459 DR_intorel *myState = (DR_intorel *) self;
460 IntoClause *into = myState->into;
461 bool is_matview;
462 List *attrList;
463 ObjectAddress intoRelationAddr;
464 Relation intoRelationDesc;
465 ListCell *lc;
466 int attnum;
468 Assert(into != NULL); /* else somebody forgot to set it */
470 /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
471 is_matview = (into->viewQuery != NULL);
474 * Build column definitions using "pre-cooked" type and collation info. If
475 * a column name list was specified in CREATE TABLE AS, override the
476 * column names derived from the query. (Too few column names are OK, too
477 * many are not.)
479 attrList = NIL;
480 lc = list_head(into->colNames);
481 for (attnum = 0; attnum < typeinfo->natts; attnum++)
483 Form_pg_attribute attribute = TupleDescAttr(typeinfo, attnum);
484 ColumnDef *col;
485 char *colname;
487 if (lc)
489 colname = strVal(lfirst(lc));
490 lc = lnext(into->colNames, lc);
492 else
493 colname = NameStr(attribute->attname);
495 col = makeColumnDef(colname,
496 attribute->atttypid,
497 attribute->atttypmod,
498 attribute->attcollation);
501 * It's possible that the column is of a collatable type but the
502 * collation could not be resolved, so double-check. (We must check
503 * this here because DefineRelation would adopt the type's default
504 * collation rather than complaining.)
506 if (!OidIsValid(col->collOid) &&
507 type_is_collatable(col->typeName->typeOid))
508 ereport(ERROR,
509 (errcode(ERRCODE_INDETERMINATE_COLLATION),
510 errmsg("no collation was derived for column \"%s\" with collatable type %s",
511 col->colname,
512 format_type_be(col->typeName->typeOid)),
513 errhint("Use the COLLATE clause to set the collation explicitly.")));
515 attrList = lappend(attrList, col);
518 if (lc != NULL)
519 ereport(ERROR,
520 (errcode(ERRCODE_SYNTAX_ERROR),
521 errmsg("too many column names were specified")));
524 * Actually create the target table
526 intoRelationAddr = create_ctas_internal(attrList, into);
529 * Finally we can open the target table
531 intoRelationDesc = table_open(intoRelationAddr.objectId, AccessExclusiveLock);
534 * Make sure the constructed table does not have RLS enabled.
536 * check_enable_rls() will ereport(ERROR) itself if the user has requested
537 * something invalid, and otherwise will return RLS_ENABLED if RLS should
538 * be enabled here. We don't actually support that currently, so throw
539 * our own ereport(ERROR) if that happens.
541 if (check_enable_rls(intoRelationAddr.objectId, InvalidOid, false) == RLS_ENABLED)
542 ereport(ERROR,
543 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
544 errmsg("policies not yet implemented for this command")));
547 * Tentatively mark the target as populated, if it's a matview and we're
548 * going to fill it; otherwise, no change needed.
550 if (is_matview && !into->skipData)
551 SetMatViewPopulatedState(intoRelationDesc, true);
554 * Fill private fields of myState for use by later routines
556 myState->rel = intoRelationDesc;
557 myState->reladdr = intoRelationAddr;
558 myState->output_cid = GetCurrentCommandId(true);
559 myState->ti_options = TABLE_INSERT_SKIP_FSM;
562 * If WITH NO DATA is specified, there is no need to set up the state for
563 * bulk inserts as there are no tuples to insert.
565 if (!into->skipData)
566 myState->bistate = GetBulkInsertState();
567 else
568 myState->bistate = NULL;
571 * Valid smgr_targblock implies something already wrote to the relation.
572 * This may be harmless, but this function hasn't planned for it.
574 Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
578 * intorel_receive --- receive one tuple
580 static bool
581 intorel_receive(TupleTableSlot *slot, DestReceiver *self)
583 DR_intorel *myState = (DR_intorel *) self;
585 /* Nothing to insert if WITH NO DATA is specified. */
586 if (!myState->into->skipData)
589 * Note that the input slot might not be of the type of the target
590 * relation. That's supported by table_tuple_insert(), but slightly
591 * less efficient than inserting with the right slot - but the
592 * alternative would be to copy into a slot of the right type, which
593 * would not be cheap either. This also doesn't allow accessing per-AM
594 * data (say a tuple's xmin), but since we don't do that here...
596 table_tuple_insert(myState->rel,
597 slot,
598 myState->output_cid,
599 myState->ti_options,
600 myState->bistate);
603 /* We know this is a newly created relation, so there are no indexes */
605 return true;
609 * intorel_shutdown --- executor end
611 static void
612 intorel_shutdown(DestReceiver *self)
614 DR_intorel *myState = (DR_intorel *) self;
615 IntoClause *into = myState->into;
617 if (!into->skipData)
619 FreeBulkInsertState(myState->bistate);
620 table_finish_bulk_insert(myState->rel, myState->ti_options);
623 /* close rel, but keep lock until commit */
624 table_close(myState->rel, NoLock);
625 myState->rel = NULL;
629 * intorel_destroy --- release DestReceiver object
631 static void
632 intorel_destroy(DestReceiver *self)
634 pfree(self);