LCOV - code coverage report
Current view: top level - src/backend/commands - createas.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 97.8 % 226 221
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 66.1 % 112 74

             Branch data     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                 :         206 : create_ctas_internal(List *attrList, IntoClause *into)
      83                 :             : {
      84                 :         206 :         CreateStmt *create = makeNode(CreateStmt);
      85                 :         206 :         bool            is_matview;
      86                 :         206 :         char            relkind;
      87                 :         206 :         Datum           toast_options;
      88                 :         206 :         const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
      89                 :             :         ObjectAddress intoRelationAddr;
      90                 :             : 
      91                 :             :         /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
      92                 :         206 :         is_matview = (into->viewQuery != NULL);
      93                 :         206 :         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                 :         206 :         create->relation = into->rel;
     100                 :         206 :         create->tableElts = attrList;
     101                 :         206 :         create->inhRelations = NIL;
     102                 :         206 :         create->ofTypename = NULL;
     103                 :         206 :         create->constraints = NIL;
     104                 :         206 :         create->options = into->options;
     105                 :         206 :         create->oncommit = into->onCommit;
     106                 :         206 :         create->tablespacename = into->tableSpaceName;
     107                 :         206 :         create->if_not_exists = false;
     108                 :         206 :         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                 :         206 :         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                 :         206 :         CommandCounterIncrement();
     122                 :             : 
     123                 :             :         /* parse and validate reloptions for the toast table */
     124                 :         206 :         toast_options = transformRelOptions((Datum) 0,
     125                 :         206 :                                                                                 create->options,
     126                 :             :                                                                                 "toast",
     127                 :         206 :                                                                                 validnsps,
     128                 :             :                                                                                 true, false);
     129                 :             : 
     130                 :         206 :         (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
     131                 :             : 
     132                 :         206 :         NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
     133                 :             : 
     134                 :             :         /* Create the "view" part of a materialized view. */
     135         [ +  + ]:         206 :         if (is_matview)
     136                 :             :         {
     137                 :             :                 /* StoreViewQuery scribbles on tree, so make a copy */
     138                 :          50 :                 Query      *query = copyObject(into->viewQuery);
     139                 :             : 
     140                 :          50 :                 StoreViewQuery(intoRelationAddr.objectId, query, false);
     141                 :          50 :                 CommandCounterIncrement();
     142                 :          50 :         }
     143                 :             : 
     144                 :             :         return intoRelationAddr;
     145                 :         206 : }
     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                 :          54 : create_ctas_nodata(List *tlist, IntoClause *into)
     156                 :             : {
     157                 :          54 :         List       *attrList;
     158                 :          54 :         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                 :          54 :         attrList = NIL;
     167                 :          54 :         lc = list_head(into->colNames);
     168   [ +  -  +  +  :         140 :         foreach(t, tlist)
                   +  + ]
     169                 :             :         {
     170                 :          86 :                 TargetEntry *tle = (TargetEntry *) lfirst(t);
     171                 :             : 
     172         [ -  + ]:          86 :                 if (!tle->resjunk)
     173                 :             :                 {
     174                 :          86 :                         ColumnDef  *col;
     175                 :          86 :                         char       *colname;
     176                 :             : 
     177         [ +  + ]:          86 :                         if (lc)
     178                 :             :                         {
     179                 :          19 :                                 colname = strVal(lfirst(lc));
     180                 :          19 :                                 lc = lnext(into->colNames, lc);
     181                 :          19 :                         }
     182                 :             :                         else
     183                 :          67 :                                 colname = tle->resname;
     184                 :             : 
     185                 :         172 :                         col = makeColumnDef(colname,
     186                 :          86 :                                                                 exprType((Node *) tle->expr),
     187                 :          86 :                                                                 exprTypmod((Node *) tle->expr),
     188                 :          86 :                                                                 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   [ +  +  +  - ]:          86 :                         if (!OidIsValid(col->collOid) &&
     197                 :          72 :                                 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                 :          86 :                         attrList = lappend(attrList, col);
     206                 :          86 :                 }
     207                 :          86 :         }
     208                 :             : 
     209         [ +  + ]:          54 :         if (lc != NULL)
     210   [ +  -  +  - ]:           3 :                 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                 :          51 :         return create_ctas_internal(attrList, into);
     216                 :          51 : }
     217                 :             : 
     218                 :             : 
     219                 :             : /*
     220                 :             :  * ExecCreateTableAs -- execute a CREATE TABLE AS command
     221                 :             :  */
     222                 :             : ObjectAddress
     223                 :         206 : ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
     224                 :             :                                   ParamListInfo params, QueryEnvironment *queryEnv,
     225                 :             :                                   QueryCompletion *qc)
     226                 :             : {
     227                 :         206 :         Query      *query = castNode(Query, stmt->query);
     228                 :         206 :         IntoClause *into = stmt->into;
     229                 :         206 :         JumbleState *jstate = NULL;
     230                 :         206 :         bool            is_matview = (into->viewQuery != NULL);
     231                 :         206 :         bool            do_refresh = false;
     232                 :         206 :         DestReceiver *dest;
     233                 :         206 :         ObjectAddress address;
     234                 :             : 
     235                 :             :         /* Check if the relation exists or not */
     236         [ +  + ]:         206 :         if (CreateTableAsRelExists(stmt))
     237                 :           7 :                 return InvalidObjectAddress;
     238                 :             : 
     239                 :             :         /*
     240                 :             :          * Create the tuple receiver object and insert info it will need
     241                 :             :          */
     242                 :         199 :         dest = CreateIntoRelDestReceiver(into);
     243                 :             : 
     244                 :             :         /* Query contained by CTAS needs to be jumbled if requested */
     245         [ +  - ]:         199 :         if (IsQueryIdEnabled())
     246                 :           0 :                 jstate = JumbleQuery(query);
     247                 :             : 
     248         [ +  - ]:         199 :         if (post_parse_analyze_hook)
     249                 :           0 :                 (*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   [ +  +  -  + ]:         199 :         if (query->commandType == CMD_UTILITY &&
     256                 :           7 :                 IsA(query->utilityStmt, ExecuteStmt))
     257                 :             :         {
     258                 :           7 :                 ExecuteStmt *estmt = castNode(ExecuteStmt, query->utilityStmt);
     259                 :             : 
     260         [ +  - ]:           7 :                 Assert(!is_matview);    /* excluded by syntax */
     261                 :           7 :                 ExecuteQuery(pstate, estmt, into, params, dest, qc);
     262                 :             : 
     263                 :             :                 /* get object address that intorel_startup saved for us */
     264                 :           7 :                 address = ((DR_intorel *) dest)->reladdr;
     265                 :             : 
     266                 :           7 :                 return address;
     267                 :           7 :         }
     268         [ +  - ]:         192 :         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         [ +  + ]:         192 :         if (is_matview)
     275                 :             :         {
     276                 :          50 :                 do_refresh = !into->skipData;
     277                 :          50 :                 into->skipData = true;
     278                 :          50 :         }
     279                 :             : 
     280         [ +  + ]:         192 :         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                 :          50 :                 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         [ +  + ]:          50 :                 if (do_refresh)
     296                 :          76 :                         RefreshMatViewByOid(address.objectId, true, false, false,
     297                 :          38 :                                                                 pstate->p_sourcetext, qc);
     298                 :             : 
     299                 :          50 :         }
     300                 :             :         else
     301                 :             :         {
     302                 :         142 :                 List       *rewritten;
     303                 :         142 :                 PlannedStmt *plan;
     304                 :         142 :                 QueryDesc  *queryDesc;
     305                 :             : 
     306         [ +  - ]:         142 :                 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                 :         142 :                 rewritten = QueryRewrite(query);
     315                 :             : 
     316                 :             :                 /* SELECT should never rewrite to more or less than one SELECT query */
     317         [ +  - ]:         142 :                 if (list_length(rewritten) != 1)
     318   [ #  #  #  # ]:           0 :                         elog(ERROR, "unexpected rewrite result for CREATE TABLE AS SELECT");
     319                 :         142 :                 query = linitial_node(Query, rewritten);
     320         [ +  - ]:         142 :                 Assert(query->commandType == CMD_SELECT);
     321                 :             : 
     322                 :             :                 /* plan the query */
     323                 :         284 :                 plan = pg_plan_query(query, pstate->p_sourcetext,
     324                 :         142 :                                                          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                 :         142 :                 PushCopiedSnapshot(GetActiveSnapshot());
     334                 :         142 :                 UpdateActiveSnapshotCommandId();
     335                 :             : 
     336                 :             :                 /* Create a QueryDesc, redirecting output to our tuple receiver */
     337                 :         284 :                 queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
     338                 :         142 :                                                                         GetActiveSnapshot(), InvalidSnapshot,
     339                 :         142 :                                                                         dest, params, queryEnv, 0);
     340                 :             : 
     341                 :             :                 /* call ExecutorStart to prepare the plan for execution */
     342                 :         142 :                 ExecutorStart(queryDesc, GetIntoRelEFlags(into));
     343                 :             : 
     344                 :             :                 /* run the plan to completion */
     345                 :         142 :                 ExecutorRun(queryDesc, ForwardScanDirection, 0);
     346                 :             : 
     347                 :             :                 /* save the rowcount if we're given a qc to fill */
     348         [ +  + ]:         142 :                 if (qc)
     349                 :         135 :                         SetQueryCompletion(qc, CMDTAG_SELECT, queryDesc->estate->es_processed);
     350                 :             : 
     351                 :             :                 /* get object address that intorel_startup saved for us */
     352                 :         142 :                 address = ((DR_intorel *) dest)->reladdr;
     353                 :             : 
     354                 :             :                 /* and clean up */
     355                 :         142 :                 ExecutorFinish(queryDesc);
     356                 :         142 :                 ExecutorEnd(queryDesc);
     357                 :             : 
     358                 :         142 :                 FreeQueryDesc(queryDesc);
     359                 :             : 
     360                 :         142 :                 PopActiveSnapshot();
     361                 :         142 :         }
     362                 :             : 
     363                 :         192 :         return address;
     364                 :         206 : }
     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                 :         166 : GetIntoRelEFlags(IntoClause *intoClause)
     376                 :             : {
     377                 :         166 :         int                     flags = 0;
     378                 :             : 
     379         [ +  + ]:         166 :         if (intoClause->skipData)
     380                 :           7 :                 flags |= EXEC_FLAG_WITH_NO_DATA;
     381                 :             : 
     382                 :         332 :         return flags;
     383                 :         166 : }
     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                 :         244 : CreateTableAsRelExists(CreateTableAsStmt *ctas)
     394                 :             : {
     395                 :         244 :         Oid                     nspid;
     396                 :         244 :         Oid                     oldrelid;
     397                 :         244 :         ObjectAddress address;
     398                 :         244 :         IntoClause *into = ctas->into;
     399                 :             : 
     400                 :         244 :         nspid = RangeVarGetCreationNamespace(into->rel);
     401                 :             : 
     402                 :         244 :         oldrelid = get_relname_relid(into->rel->relname, nspid);
     403         [ +  + ]:         244 :         if (OidIsValid(oldrelid))
     404                 :             :         {
     405         [ +  + ]:          24 :                 if (!ctas->if_not_exists)
     406   [ +  -  +  - ]:          12 :                         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                 :          12 :                 ObjectAddressSet(address, RelationRelationId, oldrelid);
     418                 :          12 :                 checkMembershipInCurrentExtension(&address);
     419                 :             : 
     420                 :             :                 /* OK to skip */
     421   [ -  +  +  - ]:          12 :                 ereport(NOTICE,
     422                 :             :                                 (errcode(ERRCODE_DUPLICATE_TABLE),
     423                 :             :                                  errmsg("relation \"%s\" already exists, skipping",
     424                 :             :                                                 into->rel->relname)));
     425                 :          12 :                 return true;
     426                 :             :         }
     427                 :             : 
     428                 :             :         /* Relation does not exist, it can be created */
     429                 :         220 :         return false;
     430                 :         232 : }
     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                 :         220 : CreateIntoRelDestReceiver(IntoClause *intoClause)
     441                 :             : {
     442                 :         220 :         DR_intorel *self = palloc0_object(DR_intorel);
     443                 :             : 
     444                 :         220 :         self->pub.receiveSlot = intorel_receive;
     445                 :         220 :         self->pub.rStartup = intorel_startup;
     446                 :         220 :         self->pub.rShutdown = intorel_shutdown;
     447                 :         220 :         self->pub.rDestroy = intorel_destroy;
     448                 :         220 :         self->pub.mydest = DestIntoRel;
     449                 :         220 :         self->into = intoClause;
     450                 :             :         /* other private fields will be set during intorel_startup */
     451                 :             : 
     452                 :         440 :         return (DestReceiver *) self;
     453                 :         220 : }
     454                 :             : 
     455                 :             : /*
     456                 :             :  * intorel_startup --- executor startup
     457                 :             :  */
     458                 :             : static void
     459                 :         155 : intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
     460                 :             : {
     461                 :         155 :         DR_intorel *myState = (DR_intorel *) self;
     462                 :         155 :         IntoClause *into = myState->into;
     463                 :         155 :         bool            is_matview;
     464                 :         155 :         List       *attrList;
     465                 :         155 :         ObjectAddress intoRelationAddr;
     466                 :         155 :         Relation        intoRelationDesc;
     467                 :         155 :         ListCell   *lc;
     468                 :         155 :         int                     attnum;
     469                 :             : 
     470         [ +  - ]:         155 :         Assert(into != NULL);           /* else somebody forgot to set it */
     471                 :             : 
     472                 :             :         /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
     473                 :         155 :         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                 :         155 :         attrList = NIL;
     482                 :         155 :         lc = list_head(into->colNames);
     483         [ +  + ]:         704 :         for (attnum = 0; attnum < typeinfo->natts; attnum++)
     484                 :             :         {
     485                 :         551 :                 Form_pg_attribute attribute = TupleDescAttr(typeinfo, attnum);
     486                 :         551 :                 ColumnDef  *col;
     487                 :         551 :                 char       *colname;
     488                 :             : 
     489         [ +  + ]:         551 :                 if (lc)
     490                 :             :                 {
     491                 :          35 :                         colname = strVal(lfirst(lc));
     492                 :          35 :                         lc = lnext(into->colNames, lc);
     493                 :          35 :                 }
     494                 :             :                 else
     495                 :         516 :                         colname = NameStr(attribute->attname);
     496                 :             : 
     497                 :        1102 :                 col = makeColumnDef(colname,
     498                 :         551 :                                                         attribute->atttypid,
     499                 :         551 :                                                         attribute->atttypmod,
     500                 :         551 :                                                         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   [ +  +  +  + ]:         551 :                 if (!OidIsValid(col->collOid) &&
     509                 :         468 :                         type_is_collatable(col->typeName->typeOid))
     510   [ +  -  +  - ]:           2 :                         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                 :         549 :                 attrList = lappend(attrList, col);
     518                 :         549 :         }
     519                 :             : 
     520         [ +  + ]:         153 :         if (lc != NULL)
     521   [ +  -  +  - ]:           1 :                 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                 :         152 :         intoRelationAddr = create_ctas_internal(attrList, into);
     529                 :             : 
     530                 :             :         /*
     531                 :             :          * Finally we can open the target table
     532                 :             :          */
     533                 :         152 :         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         [ +  - ]:         152 :         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   [ +  +  +  + ]:         152 :         if (is_matview && !into->skipData)
     553                 :           1 :                 SetMatViewPopulatedState(intoRelationDesc, true);
     554                 :             : 
     555                 :             :         /*
     556                 :             :          * Fill private fields of myState for use by later routines
     557                 :             :          */
     558                 :         152 :         myState->rel = intoRelationDesc;
     559                 :         152 :         myState->reladdr = intoRelationAddr;
     560                 :         152 :         myState->output_cid = GetCurrentCommandId(true);
     561                 :         152 :         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         [ +  + ]:         152 :         if (!into->skipData)
     568                 :         146 :                 myState->bistate = GetBulkInsertState();
     569                 :             :         else
     570                 :           6 :                 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   [ -  +  +  - ]:         152 :         Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
     577                 :         152 : }
     578                 :             : 
     579                 :             : /*
     580                 :             :  * intorel_receive --- receive one tuple
     581                 :             :  */
     582                 :             : static bool
     583                 :      309637 : intorel_receive(TupleTableSlot *slot, DestReceiver *self)
     584                 :             : {
     585                 :      309637 :         DR_intorel *myState = (DR_intorel *) self;
     586                 :             : 
     587                 :             :         /* Nothing to insert if WITH NO DATA is specified. */
     588         [ -  + ]:      309637 :         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                 :      619274 :                 table_tuple_insert(myState->rel,
     599                 :      309637 :                                                    slot,
     600                 :      309637 :                                                    myState->output_cid,
     601                 :      309637 :                                                    myState->ti_options,
     602                 :      309637 :                                                    myState->bistate);
     603                 :      309637 :         }
     604                 :             : 
     605                 :             :         /* We know this is a newly created relation, so there are no indexes */
     606                 :             : 
     607                 :      309637 :         return true;
     608                 :      309637 : }
     609                 :             : 
     610                 :             : /*
     611                 :             :  * intorel_shutdown --- executor end
     612                 :             :  */
     613                 :             : static void
     614                 :         152 : intorel_shutdown(DestReceiver *self)
     615                 :             : {
     616                 :         152 :         DR_intorel *myState = (DR_intorel *) self;
     617                 :         152 :         IntoClause *into = myState->into;
     618                 :             : 
     619         [ +  + ]:         152 :         if (!into->skipData)
     620                 :             :         {
     621                 :         146 :                 FreeBulkInsertState(myState->bistate);
     622                 :         146 :                 table_finish_bulk_insert(myState->rel, myState->ti_options);
     623                 :         146 :         }
     624                 :             : 
     625                 :             :         /* close rel, but keep lock until commit */
     626                 :         152 :         table_close(myState->rel, NoLock);
     627                 :         152 :         myState->rel = NULL;
     628                 :         152 : }
     629                 :             : 
     630                 :             : /*
     631                 :             :  * intorel_destroy --- release DestReceiver object
     632                 :             :  */
     633                 :             : static void
     634                 :          17 : intorel_destroy(DestReceiver *self)
     635                 :             : {
     636                 :          17 :         pfree(self);
     637                 :          17 : }
        

Generated by: LCOV version 2.3.2-1