LCOV - code coverage report
Current view: top level - src/backend/commands - createas.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.2 % 167 164
Test Date: 2026-03-10 08:14:43 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            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-2026, 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
      82          871 : create_ctas_internal(List *attrList, IntoClause *into)
      83              : {
      84          871 :     CreateStmt *create = makeNode(CreateStmt);
      85              :     bool        is_matview;
      86              :     char        relkind;
      87              :     Datum       toast_options;
      88          871 :     const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
      89              :     ObjectAddress intoRelationAddr;
      90              : 
      91              :     /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
      92          871 :     is_matview = (into->viewQuery != NULL);
      93          871 :     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          871 :     create->relation = into->rel;
     100          871 :     create->tableElts = attrList;
     101          871 :     create->inhRelations = NIL;
     102          871 :     create->ofTypename = NULL;
     103          871 :     create->constraints = NIL;
     104          871 :     create->options = into->options;
     105          871 :     create->oncommit = into->onCommit;
     106          871 :     create->tablespacename = into->tableSpaceName;
     107          871 :     create->if_not_exists = false;
     108          871 :     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              :      */
     114          871 :     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              :      */
     121          862 :     CommandCounterIncrement();
     122              : 
     123              :     /* parse and validate reloptions for the toast table */
     124          862 :     toast_options = transformRelOptions((Datum) 0,
     125              :                                         create->options,
     126              :                                         "toast",
     127              :                                         validnsps,
     128              :                                         true, false);
     129              : 
     130          862 :     (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
     131              : 
     132          862 :     NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
     133              : 
     134              :     /* Create the "view" part of a materialized view. */
     135          862 :     if (is_matview)
     136              :     {
     137              :         /* StoreViewQuery scribbles on tree, so make a copy */
     138          228 :         Query      *query = copyObject(into->viewQuery);
     139              : 
     140          228 :         StoreViewQuery(intoRelationAddr.objectId, query, false);
     141          228 :         CommandCounterIncrement();
     142              :     }
     143              : 
     144          862 :     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          242 : 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          242 :     attrList = NIL;
     167          242 :     lc = list_head(into->colNames);
     168          769 :     foreach(t, tlist)
     169              :     {
     170          527 :         TargetEntry *tle = (TargetEntry *) lfirst(t);
     171              : 
     172          527 :         if (!tle->resjunk)
     173              :         {
     174              :             ColumnDef  *col;
     175              :             char       *colname;
     176              : 
     177          527 :             if (lc)
     178              :             {
     179           62 :                 colname = strVal(lfirst(lc));
     180           62 :                 lc = lnext(into->colNames, lc);
     181              :             }
     182              :             else
     183          465 :                 colname = tle->resname;
     184              : 
     185          527 :             col = makeColumnDef(colname,
     186          527 :                                 exprType((Node *) tle->expr),
     187          527 :                                 exprTypmod((Node *) tle->expr),
     188          527 :                                 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          962 :             if (!OidIsValid(col->collOid) &&
     197          435 :                 type_is_collatable(col->typeName->typeOid))
     198            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              : 
     205          527 :             attrList = lappend(attrList, col);
     206              :         }
     207              :     }
     208              : 
     209          242 :     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          233 :     return create_ctas_internal(attrList, into);
     216              : }
     217              : 
     218              : 
     219              : /*
     220              :  * ExecCreateTableAs -- execute a CREATE TABLE AS command
     221              :  */
     222              : ObjectAddress
     223          908 : ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
     224              :                   ParamListInfo params, QueryEnvironment *queryEnv,
     225              :                   QueryCompletion *qc)
     226              : {
     227          908 :     Query      *query = castNode(Query, stmt->query);
     228          908 :     IntoClause *into = stmt->into;
     229          908 :     JumbleState *jstate = NULL;
     230          908 :     bool        is_matview = (into->viewQuery != NULL);
     231          908 :     bool        do_refresh = false;
     232              :     DestReceiver *dest;
     233              :     ObjectAddress address;
     234              : 
     235              :     /* Check if the relation exists or not */
     236          908 :     if (CreateTableAsRelExists(stmt))
     237           23 :         return InvalidObjectAddress;
     238              : 
     239              :     /*
     240              :      * Create the tuple receiver object and insert info it will need
     241              :      */
     242          862 :     dest = CreateIntoRelDestReceiver(into);
     243              : 
     244              :     /* Query contained by CTAS needs to be jumbled if requested */
     245          862 :     if (IsQueryIdEnabled())
     246          219 :         jstate = JumbleQuery(query);
     247              : 
     248          862 :     if (post_parse_analyze_hook)
     249          219 :         (*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              :      */
     255          862 :     if (query->commandType == CMD_UTILITY &&
     256           23 :         IsA(query->utilityStmt, ExecuteStmt))
     257              :     {
     258           23 :         ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
     259              : 
     260              :         Assert(!is_matview);    /* excluded by syntax */
     261           23 :         ExecuteQuery(pstate, estmt, into, params, dest, qc);
     262              : 
     263              :         /* get object address that intorel_startup saved for us */
     264           23 :         address = ((DR_intorel *) dest)->reladdr;
     265              : 
     266           23 :         return address;
     267              :     }
     268              :     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              :      */
     274          839 :     if (is_matview)
     275              :     {
     276          228 :         do_refresh = !into->skipData;
     277          228 :         into->skipData = true;
     278              :     }
     279              : 
     280          839 :     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          242 :         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              :          */
     295          233 :         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              :         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              :          */
     314          597 :         rewritten = QueryRewrite(query);
     315              : 
     316              :         /* SELECT should never rewrite to more or less than one SELECT query */
     317          597 :         if (list_length(rewritten) != 1)
     318            0 :             elog(ERROR, "unexpected rewrite result for CREATE TABLE AS SELECT");
     319          597 :         query = linitial_node(Query, rewritten);
     320              :         Assert(query->commandType == CMD_SELECT);
     321              : 
     322              :         /* plan the query */
     323          597 :         plan = pg_plan_query(query, pstate->p_sourcetext,
     324              :                              CURSOR_OPT_PARALLEL_OK, params, NULL);
     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              :          */
     333          597 :         PushCopiedSnapshot(GetActiveSnapshot());
     334          597 :         UpdateActiveSnapshotCommandId();
     335              : 
     336              :         /* Create a QueryDesc, redirecting output to our tuple receiver */
     337          597 :         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          597 :         ExecutorStart(queryDesc, GetIntoRelEFlags(into));
     343              : 
     344              :         /* run the plan to completion */
     345          597 :         ExecutorRun(queryDesc, ForwardScanDirection, 0);
     346              : 
     347              :         /* save the rowcount if we're given a qc to fill */
     348          579 :         if (qc)
     349          575 :             SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
     350              : 
     351              :         /* get object address that intorel_startup saved for us */
     352          579 :         address = ((DR_intorel *) dest)->reladdr;
     353              : 
     354              :         /* and clean up */
     355          579 :         ExecutorFinish(queryDesc);
     356          579 :         ExecutorEnd(queryDesc);
     357              : 
     358          579 :         FreeQueryDesc(queryDesc);
     359              : 
     360          579 :         PopActiveSnapshot();
     361              :     }
     362              : 
     363          809 :     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
     375          673 : GetIntoRelEFlags(IntoClause *intoClause)
     376              : {
     377          673 :     int         flags = 0;
     378              : 
     379          673 :     if (intoClause->skipData)
     380           21 :         flags |= EXEC_FLAG_WITH_NO_DATA;
     381              : 
     382          673 :     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
     393          991 : CreateTableAsRelExists(CreateTableAsStmt *ctas)
     394              : {
     395              :     Oid         nspid;
     396              :     Oid         oldrelid;
     397              :     ObjectAddress address;
     398          991 :     IntoClause *into = ctas->into;
     399              : 
     400          991 :     nspid = RangeVarGetCreationNamespace(into->rel);
     401              : 
     402          991 :     oldrelid = get_relname_relid(into->rel->relname, nspid);
     403          991 :     if (OidIsValid(oldrelid))
     404              :     {
     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              :          */
     417           40 :         ObjectAddressSet(address, RelationRelationId, oldrelid);
     418           40 :         checkMembershipInCurrentExtension(&address);
     419              : 
     420              :         /* OK to skip */
     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          915 :     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 *
     440          915 : CreateIntoRelDestReceiver(IntoClause *intoClause)
     441              : {
     442          915 :     DR_intorel *self = palloc0_object(DR_intorel);
     443              : 
     444          915 :     self->pub.receiveSlot = intorel_receive;
     445          915 :     self->pub.rStartup = intorel_startup;
     446          915 :     self->pub.rShutdown = intorel_shutdown;
     447          915 :     self->pub.rDestroy = intorel_destroy;
     448          915 :     self->pub.mydest = DestIntoRel;
     449          915 :     self->into = intoClause;
     450              :     /* other private fields will be set during intorel_startup */
     451              : 
     452          915 :     return (DestReceiver *) self;
     453              : }
     454              : 
     455              : /*
     456              :  * intorel_startup --- executor startup
     457              :  */
     458              : static void
     459          647 : intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
     460              : {
     461          647 :     DR_intorel *myState = (DR_intorel *) self;
     462          647 :     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              :     Assert(into != NULL);       /* else somebody forgot to set it */
     471              : 
     472              :     /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
     473          647 :     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              :      */
     481          647 :     attrList = NIL;
     482          647 :     lc = list_head(into->colNames);
     483         2563 :     for (attnum = 0; attnum < typeinfo->natts; attnum++)
     484              :     {
     485         1922 :         Form_pg_attribute attribute = TupleDescAttr(typeinfo, attnum);
     486              :         ColumnDef  *col;
     487              :         char       *colname;
     488              : 
     489         1922 :         if (lc)
     490              :         {
     491          108 :             colname = strVal(lfirst(lc));
     492          108 :             lc = lnext(into->colNames, lc);
     493              :         }
     494              :         else
     495         1814 :             colname = NameStr(attribute->attname);
     496              : 
     497         1922 :         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              :          */
     508         3575 :         if (!OidIsValid(col->collOid) &&
     509         1653 :             type_is_collatable(col->typeName->typeOid))
     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              : 
     517         1916 :         attrList = lappend(attrList, col);
     518              :     }
     519              : 
     520          641 :     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              :      */
     528          638 :     intoRelationAddr = create_ctas_internal(attrList, into);
     529              : 
     530              :     /*
     531              :      * Finally we can open the target table
     532              :      */
     533          629 :     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              :      */
     543          629 :     if (check_enable_rls(intoRelationAddr.objectId, InvalidOid, false) == RLS_ENABLED)
     544            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              :      */
     552          629 :     if (is_matview && !into->skipData)
     553            3 :         SetMatViewPopulatedState(intoRelationDesc, true);
     554              : 
     555              :     /*
     556              :      * Fill private fields of myState for use by later routines
     557              :      */
     558          629 :     myState->rel = intoRelationDesc;
     559          629 :     myState->reladdr = intoRelationAddr;
     560          629 :     myState->output_cid = GetCurrentCommandId(true);
     561          629 :     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              :      */
     567          629 :     if (!into->skipData)
     568          611 :         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              :      */
     576              :     Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
     577          629 : }
     578              : 
     579              : /*
     580              :  * intorel_receive --- receive one tuple
     581              :  */
     582              : static bool
     583      1132177 : intorel_receive(TupleTableSlot *slot, DestReceiver *self)
     584              : {
     585      1132177 :     DR_intorel *myState = (DR_intorel *) self;
     586              : 
     587              :     /* Nothing to insert if WITH NO DATA is specified. */
     588      1132177 :     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      1132177 :         table_tuple_insert(myState->rel,
     599              :                            slot,
     600              :                            myState->output_cid,
     601              :                            myState->ti_options,
     602      1132177 :                            myState->bistate);
     603              :     }
     604              : 
     605              :     /* We know this is a newly created relation, so there are no indexes */
     606              : 
     607      1132177 :     return true;
     608              : }
     609              : 
     610              : /*
     611              :  * intorel_shutdown --- executor end
     612              :  */
     613              : static void
     614          629 : intorel_shutdown(DestReceiver *self)
     615              : {
     616          629 :     DR_intorel *myState = (DR_intorel *) self;
     617          629 :     IntoClause *into = myState->into;
     618              : 
     619          629 :     if (!into->skipData)
     620              :     {
     621          611 :         FreeBulkInsertState(myState->bistate);
     622          611 :         table_finish_bulk_insert(myState->rel, myState->ti_options);
     623              :     }
     624              : 
     625              :     /* close rel, but keep lock until commit */
     626          629 :     table_close(myState->rel, NoLock);
     627          629 :     myState->rel = NULL;
     628          629 : }
     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 : }
        

Generated by: LCOV version 2.0-1