Header And Logo

PostgreSQL
| The world's most advanced open source database.

createas.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * createas.c
00004  *    Execution of CREATE TABLE ... AS, a/k/a SELECT INTO.
00005  *    Since CREATE MATERIALIZED VIEW shares syntax and most behaviors,
00006  *    we implement that here, too.
00007  *
00008  * We implement this by diverting the query's normal output to a
00009  * specialized DestReceiver type.
00010  *
00011  * Formerly, CTAS was implemented as a variant of SELECT, which led
00012  * to assorted legacy behaviors that we still try to preserve, notably that
00013  * we must return a tuples-processed count in the completionTag.
00014  *
00015  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00016  * Portions Copyright (c) 1994, Regents of the University of California
00017  *
00018  *
00019  * IDENTIFICATION
00020  *    src/backend/commands/createas.c
00021  *
00022  *-------------------------------------------------------------------------
00023  */
00024 #include "postgres.h"
00025 
00026 #include "access/reloptions.h"
00027 #include "access/htup_details.h"
00028 #include "access/sysattr.h"
00029 #include "access/xact.h"
00030 #include "catalog/toasting.h"
00031 #include "commands/createas.h"
00032 #include "commands/matview.h"
00033 #include "commands/prepare.h"
00034 #include "commands/tablecmds.h"
00035 #include "commands/view.h"
00036 #include "parser/parse_clause.h"
00037 #include "rewrite/rewriteHandler.h"
00038 #include "storage/smgr.h"
00039 #include "tcop/tcopprot.h"
00040 #include "utils/builtins.h"
00041 #include "utils/lsyscache.h"
00042 #include "utils/rel.h"
00043 #include "utils/snapmgr.h"
00044 
00045 
00046 typedef struct
00047 {
00048     DestReceiver pub;           /* publicly-known function pointers */
00049     IntoClause *into;           /* target relation specification */
00050     /* These fields are filled by intorel_startup: */
00051     Relation    rel;            /* relation to write to */
00052     CommandId   output_cid;     /* cmin to insert in output tuples */
00053     int         hi_options;     /* heap_insert performance options */
00054     BulkInsertState bistate;    /* bulk insert state */
00055 } DR_intorel;
00056 
00057 static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
00058 static void intorel_receive(TupleTableSlot *slot, DestReceiver *self);
00059 static void intorel_shutdown(DestReceiver *self);
00060 static void intorel_destroy(DestReceiver *self);
00061 
00062 
00063 /*
00064  * ExecCreateTableAs -- execute a CREATE TABLE AS command
00065  */
00066 void
00067 ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
00068                   ParamListInfo params, char *completionTag)
00069 {
00070     Query      *query = (Query *) stmt->query;
00071     IntoClause *into = stmt->into;
00072     DestReceiver *dest;
00073     List       *rewritten;
00074     PlannedStmt *plan;
00075     QueryDesc  *queryDesc;
00076     ScanDirection dir;
00077 
00078     /*
00079      * Create the tuple receiver object and insert info it will need
00080      */
00081     dest = CreateIntoRelDestReceiver(into);
00082 
00083     /*
00084      * The contained Query could be a SELECT, or an EXECUTE utility command.
00085      * If the latter, we just pass it off to ExecuteQuery.
00086      */
00087     Assert(IsA(query, Query));
00088     if (query->commandType == CMD_UTILITY &&
00089         IsA(query->utilityStmt, ExecuteStmt))
00090     {
00091         ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
00092 
00093         ExecuteQuery(estmt, into, queryString, params, dest, completionTag);
00094 
00095         return;
00096     }
00097     Assert(query->commandType == CMD_SELECT);
00098 
00099     /*
00100      * Parse analysis was done already, but we still have to run the rule
00101      * rewriter.  We do not do AcquireRewriteLocks: we assume the query either
00102      * came straight from the parser, or suitable locks were acquired by
00103      * plancache.c.
00104      *
00105      * Because the rewriter and planner tend to scribble on the input, we make
00106      * a preliminary copy of the source querytree.  This prevents problems in
00107      * the case that CTAS is in a portal or plpgsql function and is executed
00108      * repeatedly.  (See also the same hack in EXPLAIN and PREPARE.)
00109      */
00110     rewritten = QueryRewrite((Query *) copyObject(query));
00111 
00112     /* SELECT should never rewrite to more or less than one SELECT query */
00113     if (list_length(rewritten) != 1)
00114         elog(ERROR, "unexpected rewrite result for CREATE TABLE AS SELECT");
00115     query = (Query *) linitial(rewritten);
00116     Assert(query->commandType == CMD_SELECT);
00117 
00118     /* plan the query */
00119     plan = pg_plan_query(query, 0, params);
00120 
00121     /*
00122      * Use a snapshot with an updated command ID to ensure this query sees
00123      * results of any previously executed queries.  (This could only matter if
00124      * the planner executed an allegedly-stable function that changed the
00125      * database contents, but let's do it anyway to be parallel to the EXPLAIN
00126      * code path.)
00127      */
00128     PushCopiedSnapshot(GetActiveSnapshot());
00129     UpdateActiveSnapshotCommandId();
00130 
00131     /* Create a QueryDesc, redirecting output to our tuple receiver */
00132     queryDesc = CreateQueryDesc(plan, queryString,
00133                                 GetActiveSnapshot(), InvalidSnapshot,
00134                                 dest, params, 0);
00135 
00136     /* call ExecutorStart to prepare the plan for execution */
00137     ExecutorStart(queryDesc, GetIntoRelEFlags(into));
00138 
00139     /*
00140      * Normally, we run the plan to completion; but if skipData is specified,
00141      * just do tuple receiver startup and shutdown.
00142      */
00143     if (into->skipData)
00144         dir = NoMovementScanDirection;
00145     else
00146         dir = ForwardScanDirection;
00147 
00148     /* run the plan */
00149     ExecutorRun(queryDesc, dir, 0L);
00150 
00151     /* save the rowcount if we're given a completionTag to fill */
00152     if (completionTag)
00153         snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
00154                  "SELECT %u", queryDesc->estate->es_processed);
00155 
00156     /* and clean up */
00157     ExecutorFinish(queryDesc);
00158     ExecutorEnd(queryDesc);
00159 
00160     FreeQueryDesc(queryDesc);
00161 
00162     PopActiveSnapshot();
00163 }
00164 
00165 /*
00166  * GetIntoRelEFlags --- compute executor flags needed for CREATE TABLE AS
00167  *
00168  * This is exported because EXPLAIN and PREPARE need it too.  (Note: those
00169  * callers still need to deal explicitly with the skipData flag; since they
00170  * use different methods for suppressing execution, it doesn't seem worth
00171  * trying to encapsulate that part.)
00172  */
00173 int
00174 GetIntoRelEFlags(IntoClause *intoClause)
00175 {
00176     int     flags;
00177 
00178     /*
00179      * We need to tell the executor whether it has to produce OIDs or not,
00180      * because it doesn't have enough information to do so itself (since we
00181      * can't build the target relation until after ExecutorStart).
00182      *
00183      * Disallow the OIDS option for materialized views.
00184      */
00185     if (interpretOidsOption(intoClause->options,
00186                             (intoClause->viewQuery == NULL)))
00187         flags = EXEC_FLAG_WITH_OIDS;
00188     else
00189         flags = EXEC_FLAG_WITHOUT_OIDS;
00190 
00191     if (intoClause->skipData)
00192         flags |= EXEC_FLAG_WITH_NO_DATA;
00193 
00194     return flags;
00195 }
00196 
00197 /*
00198  * CreateIntoRelDestReceiver -- create a suitable DestReceiver object
00199  *
00200  * intoClause will be NULL if called from CreateDestReceiver(), in which
00201  * case it has to be provided later.  However, it is convenient to allow
00202  * self->into to be filled in immediately for other callers.
00203  */
00204 DestReceiver *
00205 CreateIntoRelDestReceiver(IntoClause *intoClause)
00206 {
00207     DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel));
00208 
00209     self->pub.receiveSlot = intorel_receive;
00210     self->pub.rStartup = intorel_startup;
00211     self->pub.rShutdown = intorel_shutdown;
00212     self->pub.rDestroy = intorel_destroy;
00213     self->pub.mydest = DestIntoRel;
00214     self->into = intoClause;
00215     /* other private fields will be set during intorel_startup */
00216 
00217     return (DestReceiver *) self;
00218 }
00219 
00220 /*
00221  * intorel_startup --- executor startup
00222  */
00223 static void
00224 intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
00225 {
00226     DR_intorel *myState = (DR_intorel *) self;
00227     IntoClause *into = myState->into;
00228     bool        is_matview;
00229     char        relkind;
00230     CreateStmt *create;
00231     Oid         intoRelationId;
00232     Relation    intoRelationDesc;
00233     RangeTblEntry *rte;
00234     Datum       toast_options;
00235     ListCell   *lc;
00236     int         attnum;
00237     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
00238 
00239     Assert(into != NULL);       /* else somebody forgot to set it */
00240 
00241     /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
00242     is_matview = (into->viewQuery != NULL);
00243     relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION;
00244 
00245     /*
00246      * Create the target relation by faking up a CREATE TABLE parsetree and
00247      * passing it to DefineRelation.
00248      */
00249     create = makeNode(CreateStmt);
00250     create->relation = into->rel;
00251     create->tableElts = NIL;    /* will fill below */
00252     create->inhRelations = NIL;
00253     create->ofTypename = NULL;
00254     create->constraints = NIL;
00255     create->options = into->options;
00256     create->oncommit = into->onCommit;
00257     create->tablespacename = into->tableSpaceName;
00258     create->if_not_exists = false;
00259 
00260     /*
00261      * Build column definitions using "pre-cooked" type and collation info. If
00262      * a column name list was specified in CREATE TABLE AS, override the
00263      * column names derived from the query.  (Too few column names are OK, too
00264      * many are not.)
00265      */
00266     lc = list_head(into->colNames);
00267     for (attnum = 0; attnum < typeinfo->natts; attnum++)
00268     {
00269         Form_pg_attribute attribute = typeinfo->attrs[attnum];
00270         ColumnDef  *col = makeNode(ColumnDef);
00271         TypeName   *coltype = makeNode(TypeName);
00272 
00273         if (lc)
00274         {
00275             col->colname = strVal(lfirst(lc));
00276             lc = lnext(lc);
00277         }
00278         else
00279             col->colname = NameStr(attribute->attname);
00280         col->typeName = coltype;
00281         col->inhcount = 0;
00282         col->is_local = true;
00283         col->is_not_null = false;
00284         col->is_from_type = false;
00285         col->storage = 0;
00286         col->raw_default = NULL;
00287         col->cooked_default = NULL;
00288         col->collClause = NULL;
00289         col->collOid = attribute->attcollation;
00290         col->constraints = NIL;
00291         col->fdwoptions = NIL;
00292 
00293         coltype->names = NIL;
00294         coltype->typeOid = attribute->atttypid;
00295         coltype->setof = false;
00296         coltype->pct_type = false;
00297         coltype->typmods = NIL;
00298         coltype->typemod = attribute->atttypmod;
00299         coltype->arrayBounds = NIL;
00300         coltype->location = -1;
00301 
00302         /*
00303          * It's possible that the column is of a collatable type but the
00304          * collation could not be resolved, so double-check.  (We must check
00305          * this here because DefineRelation would adopt the type's default
00306          * collation rather than complaining.)
00307          */
00308         if (!OidIsValid(col->collOid) &&
00309             type_is_collatable(coltype->typeOid))
00310             ereport(ERROR,
00311                     (errcode(ERRCODE_INDETERMINATE_COLLATION),
00312                      errmsg("no collation was derived for column \"%s\" with collatable type %s",
00313                             col->colname, format_type_be(coltype->typeOid)),
00314                      errhint("Use the COLLATE clause to set the collation explicitly.")));
00315 
00316         create->tableElts = lappend(create->tableElts, col);
00317     }
00318 
00319     if (lc != NULL)
00320         ereport(ERROR,
00321                 (errcode(ERRCODE_SYNTAX_ERROR),
00322                  errmsg("too many column names were specified")));
00323 
00324     /*
00325      * Actually create the target table
00326      */
00327     intoRelationId = DefineRelation(create, relkind, InvalidOid);
00328 
00329     /*
00330      * If necessary, create a TOAST table for the target table.  Note that
00331      * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
00332      * the TOAST table will be visible for insertion.
00333      */
00334     CommandCounterIncrement();
00335 
00336     /* parse and validate reloptions for the toast table */
00337     toast_options = transformRelOptions((Datum) 0,
00338                                         create->options,
00339                                         "toast",
00340                                         validnsps,
00341                                         true, false);
00342 
00343     (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
00344 
00345     AlterTableCreateToastTable(intoRelationId, toast_options);
00346 
00347     /* Create the "view" part of a materialized view. */
00348     if (is_matview)
00349     {
00350         /* StoreViewQuery scribbles on tree, so make a copy */
00351         Query  *query = (Query *) copyObject(into->viewQuery);
00352 
00353         StoreViewQuery(intoRelationId, query, false);
00354         CommandCounterIncrement();
00355     }
00356 
00357     /*
00358      * Finally we can open the target table
00359      */
00360     intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
00361 
00362     if (is_matview && !into->skipData)
00363         /* Make sure the heap looks good even if no rows are written. */
00364         SetMatViewToPopulated(intoRelationDesc);
00365 
00366     /*
00367      * Check INSERT permission on the constructed table.
00368      *
00369      * XXX: It would arguably make sense to skip this check if into->skipData
00370      * is true.
00371      */
00372     rte = makeNode(RangeTblEntry);
00373     rte->rtekind = RTE_RELATION;
00374     rte->relid = intoRelationId;
00375     rte->relkind = relkind;
00376     rte->requiredPerms = ACL_INSERT;
00377 
00378     for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++)
00379         rte->modifiedCols = bms_add_member(rte->modifiedCols,
00380                                 attnum - FirstLowInvalidHeapAttributeNumber);
00381 
00382     ExecCheckRTPerms(list_make1(rte), true);
00383 
00384     /*
00385      * Fill private fields of myState for use by later routines
00386      */
00387     myState->rel = intoRelationDesc;
00388     myState->output_cid = GetCurrentCommandId(true);
00389 
00390     /*
00391      * We can skip WAL-logging the insertions, unless PITR or streaming
00392      * replication is in use. We can skip the FSM in any case.
00393      */
00394     myState->hi_options = HEAP_INSERT_SKIP_FSM |
00395         (XLogIsNeeded() ? 0 : HEAP_INSERT_SKIP_WAL);
00396     myState->bistate = GetBulkInsertState();
00397 
00398     /* Not using WAL requires smgr_targblock be initially invalid */
00399     Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber);
00400 }
00401 
00402 /*
00403  * intorel_receive --- receive one tuple
00404  */
00405 static void
00406 intorel_receive(TupleTableSlot *slot, DestReceiver *self)
00407 {
00408     DR_intorel *myState = (DR_intorel *) self;
00409     HeapTuple   tuple;
00410 
00411     /*
00412      * get the heap tuple out of the tuple table slot, making sure we have a
00413      * writable copy
00414      */
00415     tuple = ExecMaterializeSlot(slot);
00416 
00417     /*
00418      * force assignment of new OID (see comments in ExecInsert)
00419      */
00420     if (myState->rel->rd_rel->relhasoids)
00421         HeapTupleSetOid(tuple, InvalidOid);
00422 
00423     heap_insert(myState->rel,
00424                 tuple,
00425                 myState->output_cid,
00426                 myState->hi_options,
00427                 myState->bistate);
00428 
00429     /* We know this is a newly created relation, so there are no indexes */
00430 }
00431 
00432 /*
00433  * intorel_shutdown --- executor end
00434  */
00435 static void
00436 intorel_shutdown(DestReceiver *self)
00437 {
00438     DR_intorel *myState = (DR_intorel *) self;
00439 
00440     FreeBulkInsertState(myState->bistate);
00441 
00442     /* If we skipped using WAL, must heap_sync before commit */
00443     if (myState->hi_options & HEAP_INSERT_SKIP_WAL)
00444         heap_sync(myState->rel);
00445 
00446     /* close rel, but keep lock until commit */
00447     heap_close(myState->rel, NoLock);
00448     myState->rel = NULL;
00449 }
00450 
00451 /*
00452  * intorel_destroy --- release DestReceiver object
00453  */
00454 static void
00455 intorel_destroy(DestReceiver *self)
00456 {
00457     pfree(self);
00458 }