#include "postgres.h"
#include "access/reloptions.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/toasting.h"
#include "commands/createas.h"
#include "commands/matview.h"
#include "commands/prepare.h"
#include "commands/tablecmds.h"
#include "commands/view.h"
#include "parser/parse_clause.h"
#include "rewrite/rewriteHandler.h"
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
Go to the source code of this file.
Data Structures | |
struct | DR_intorel |
Functions | |
static void | intorel_startup (DestReceiver *self, int operation, TupleDesc typeinfo) |
static void | intorel_receive (TupleTableSlot *slot, DestReceiver *self) |
static void | intorel_shutdown (DestReceiver *self) |
static void | intorel_destroy (DestReceiver *self) |
void | ExecCreateTableAs (CreateTableAsStmt *stmt, const char *queryString, ParamListInfo params, char *completionTag) |
int | GetIntoRelEFlags (IntoClause *intoClause) |
DestReceiver * | CreateIntoRelDestReceiver (IntoClause *intoClause) |
DestReceiver* CreateIntoRelDestReceiver | ( | IntoClause * | intoClause | ) |
Definition at line 205 of file createas.c.
References palloc0().
Referenced by CreateDestReceiver(), ExecCreateTableAs(), and ExplainOnePlan().
{ DR_intorel *self = (DR_intorel *) palloc0(sizeof(DR_intorel)); self->pub.receiveSlot = intorel_receive; self->pub.rStartup = intorel_startup; self->pub.rShutdown = intorel_shutdown; self->pub.rDestroy = intorel_destroy; self->pub.mydest = DestIntoRel; self->into = intoClause; /* other private fields will be set during intorel_startup */ return (DestReceiver *) self; }
void ExecCreateTableAs | ( | CreateTableAsStmt * | stmt, | |
const char * | queryString, | |||
ParamListInfo | params, | |||
char * | completionTag | |||
) |
Definition at line 67 of file createas.c.
References Assert, CMD_SELECT, CMD_UTILITY, Query::commandType, COMPLETION_TAG_BUFSIZE, copyObject(), CreateIntoRelDestReceiver(), CreateQueryDesc(), elog, ERROR, ExecuteQuery(), ExecutorEnd(), ExecutorFinish(), ExecutorRun(), ExecutorStart(), FreeQueryDesc(), GetActiveSnapshot(), GetIntoRelEFlags(), CreateTableAsStmt::into, InvalidSnapshot, IsA, linitial, list_length(), pg_plan_query(), PopActiveSnapshot(), PushCopiedSnapshot(), CreateTableAsStmt::query, QueryRewrite(), snprintf(), UpdateActiveSnapshotCommandId(), and Query::utilityStmt.
Referenced by ProcessUtilitySlow().
{ Query *query = (Query *) stmt->query; IntoClause *into = stmt->into; DestReceiver *dest; List *rewritten; PlannedStmt *plan; QueryDesc *queryDesc; ScanDirection dir; /* * Create the tuple receiver object and insert info it will need */ dest = CreateIntoRelDestReceiver(into); /* * The contained Query could be a SELECT, or an EXECUTE utility command. * If the latter, we just pass it off to ExecuteQuery. */ Assert(IsA(query, Query)); if (query->commandType == CMD_UTILITY && IsA(query->utilityStmt, ExecuteStmt)) { ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt; ExecuteQuery(estmt, into, queryString, params, dest, completionTag); return; } Assert(query->commandType == CMD_SELECT); /* * Parse analysis was done already, but we still have to run the rule * rewriter. We do not do AcquireRewriteLocks: we assume the query either * came straight from the parser, or suitable locks were acquired by * plancache.c. * * Because the rewriter and planner tend to scribble on the input, we make * a preliminary copy of the source querytree. This prevents problems in * the case that CTAS is in a portal or plpgsql function and is executed * repeatedly. (See also the same hack in EXPLAIN and PREPARE.) */ rewritten = QueryRewrite((Query *) copyObject(query)); /* SELECT should never rewrite to more or less than one SELECT query */ if (list_length(rewritten) != 1) elog(ERROR, "unexpected rewrite result for CREATE TABLE AS SELECT"); query = (Query *) linitial(rewritten); Assert(query->commandType == CMD_SELECT); /* plan the query */ plan = pg_plan_query(query, 0, params); /* * Use a snapshot with an updated command ID to ensure this query sees * results of any previously executed queries. (This could only matter if * the planner executed an allegedly-stable function that changed the * database contents, but let's do it anyway to be parallel to the EXPLAIN * code path.) */ PushCopiedSnapshot(GetActiveSnapshot()); UpdateActiveSnapshotCommandId(); /* Create a QueryDesc, redirecting output to our tuple receiver */ queryDesc = CreateQueryDesc(plan, queryString, GetActiveSnapshot(), InvalidSnapshot, dest, params, 0); /* call ExecutorStart to prepare the plan for execution */ ExecutorStart(queryDesc, GetIntoRelEFlags(into)); /* * Normally, we run the plan to completion; but if skipData is specified, * just do tuple receiver startup and shutdown. */ if (into->skipData) dir = NoMovementScanDirection; else dir = ForwardScanDirection; /* run the plan */ ExecutorRun(queryDesc, dir, 0L); /* save the rowcount if we're given a completionTag to fill */ if (completionTag) snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "SELECT %u", queryDesc->estate->es_processed); /* and clean up */ ExecutorFinish(queryDesc); ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); PopActiveSnapshot(); }
int GetIntoRelEFlags | ( | IntoClause * | intoClause | ) |
Definition at line 174 of file createas.c.
References interpretOidsOption(), NULL, IntoClause::options, IntoClause::skipData, and IntoClause::viewQuery.
Referenced by ExecCreateTableAs(), ExecuteQuery(), and ExplainOnePlan().
{ int flags; /* * We need to tell the executor whether it has to produce OIDs or not, * because it doesn't have enough information to do so itself (since we * can't build the target relation until after ExecutorStart). * * Disallow the OIDS option for materialized views. */ if (interpretOidsOption(intoClause->options, (intoClause->viewQuery == NULL))) flags = EXEC_FLAG_WITH_OIDS; else flags = EXEC_FLAG_WITHOUT_OIDS; if (intoClause->skipData) flags |= EXEC_FLAG_WITH_NO_DATA; return flags; }
static void intorel_destroy | ( | DestReceiver * | self | ) | [static] |
static void intorel_receive | ( | TupleTableSlot * | slot, | |
DestReceiver * | self | |||
) | [static] |
Definition at line 406 of file createas.c.
References DR_intorel::bistate, ExecMaterializeSlot(), heap_insert(), HeapTupleSetOid, DR_intorel::hi_options, InvalidOid, DR_intorel::output_cid, RelationData::rd_rel, and DR_intorel::rel.
{ DR_intorel *myState = (DR_intorel *) self; HeapTuple tuple; /* * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ tuple = ExecMaterializeSlot(slot); /* * force assignment of new OID (see comments in ExecInsert) */ if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); heap_insert(myState->rel, tuple, myState->output_cid, myState->hi_options, myState->bistate); /* We know this is a newly created relation, so there are no indexes */ }
static void intorel_shutdown | ( | DestReceiver * | self | ) | [static] |
Definition at line 436 of file createas.c.
References DR_intorel::bistate, FreeBulkInsertState(), heap_close, HEAP_INSERT_SKIP_WAL, heap_sync(), DR_intorel::hi_options, NoLock, and DR_intorel::rel.
{ DR_intorel *myState = (DR_intorel *) self; FreeBulkInsertState(myState->bistate); /* If we skipped using WAL, must heap_sync before commit */ if (myState->hi_options & HEAP_INSERT_SKIP_WAL) heap_sync(myState->rel); /* close rel, but keep lock until commit */ heap_close(myState->rel, NoLock); myState->rel = NULL; }
static void intorel_startup | ( | DestReceiver * | self, | |
int | operation, | |||
TupleDesc | typeinfo | |||
) | [static] |
Definition at line 224 of file createas.c.
References AccessExclusiveLock, AlterTableCreateToastTable(), TypeName::arrayBounds, Assert, tupleDesc::attrs, DR_intorel::bistate, bms_add_member(), ColumnDef::collClause, ColumnDef::collOid, ColumnDef::colname, IntoClause::colNames, CommandCounterIncrement(), ColumnDef::constraints, CreateStmt::constraints, ColumnDef::cooked_default, copyObject(), DefineRelation(), ereport, errcode(), errhint(), errmsg(), ERROR, ExecCheckRTPerms(), ColumnDef::fdwoptions, FirstLowInvalidHeapAttributeNumber, format_type_be(), GetBulkInsertState(), GetCurrentCommandId(), HEAP_INSERT_SKIP_FSM, heap_open(), heap_reloptions(), DR_intorel::hi_options, CreateStmt::if_not_exists, ColumnDef::inhcount, CreateStmt::inhRelations, DR_intorel::into, InvalidBlockNumber, InvalidOid, ColumnDef::is_from_type, ColumnDef::is_local, ColumnDef::is_not_null, lappend(), lfirst, list_head(), list_make1, lnext, TypeName::location, makeNode, RangeTblEntry::modifiedCols, TypeName::names, NameStr, tupleDesc::natts, NULL, CreateStmt::ofTypename, OidIsValid, IntoClause::onCommit, CreateStmt::oncommit, IntoClause::options, CreateStmt::options, DR_intorel::output_cid, TypeName::pct_type, ColumnDef::raw_default, RelationData::rd_att, DR_intorel::rel, IntoClause::rel, CreateStmt::relation, RelationGetTargetBlock, RangeTblEntry::relid, RangeTblEntry::relkind, RELKIND_MATVIEW, RELKIND_TOASTVALUE, RangeTblEntry::requiredPerms, RangeTblEntry::rtekind, SetMatViewToPopulated(), TypeName::setof, IntoClause::skipData, ColumnDef::storage, StoreViewQuery(), strVal, CreateStmt::tableElts, IntoClause::tableSpaceName, CreateStmt::tablespacename, transformRelOptions(), type_is_collatable(), TypeName::typemod, ColumnDef::typeName, TypeName::typeOid, TypeName::typmods, IntoClause::viewQuery, and XLogIsNeeded.
{ DR_intorel *myState = (DR_intorel *) self; IntoClause *into = myState->into; bool is_matview; char relkind; CreateStmt *create; Oid intoRelationId; Relation intoRelationDesc; RangeTblEntry *rte; Datum toast_options; ListCell *lc; int attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Assert(into != NULL); /* else somebody forgot to set it */ /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */ is_matview = (into->viewQuery != NULL); relkind = is_matview ? RELKIND_MATVIEW : RELKIND_RELATION; /* * Create the target relation by faking up a CREATE TABLE parsetree and * passing it to DefineRelation. */ create = makeNode(CreateStmt); create->relation = into->rel; create->tableElts = NIL; /* will fill below */ create->inhRelations = NIL; create->ofTypename = NULL; create->constraints = NIL; create->options = into->options; create->oncommit = into->onCommit; create->tablespacename = into->tableSpaceName; create->if_not_exists = false; /* * Build column definitions using "pre-cooked" type and collation info. If * a column name list was specified in CREATE TABLE AS, override the * column names derived from the query. (Too few column names are OK, too * many are not.) */ lc = list_head(into->colNames); for (attnum = 0; attnum < typeinfo->natts; attnum++) { Form_pg_attribute attribute = typeinfo->attrs[attnum]; ColumnDef *col = makeNode(ColumnDef); TypeName *coltype = makeNode(TypeName); if (lc) { col->colname = strVal(lfirst(lc)); lc = lnext(lc); } else col->colname = NameStr(attribute->attname); col->typeName = coltype; col->inhcount = 0; col->is_local = true; col->is_not_null = false; col->is_from_type = false; col->storage = 0; col->raw_default = NULL; col->cooked_default = NULL; col->collClause = NULL; col->collOid = attribute->attcollation; col->constraints = NIL; col->fdwoptions = NIL; coltype->names = NIL; coltype->typeOid = attribute->atttypid; coltype->setof = false; coltype->pct_type = false; coltype->typmods = NIL; coltype->typemod = attribute->atttypmod; coltype->arrayBounds = NIL; coltype->location = -1; /* * It's possible that the column is of a collatable type but the * collation could not be resolved, so double-check. (We must check * this here because DefineRelation would adopt the type's default * collation rather than complaining.) */ if (!OidIsValid(col->collOid) && type_is_collatable(coltype->typeOid)) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_COLLATION), errmsg("no collation was derived for column \"%s\" with collatable type %s", col->colname, format_type_be(coltype->typeOid)), errhint("Use the COLLATE clause to set the collation explicitly."))); create->tableElts = lappend(create->tableElts, col); } if (lc != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too many column names were specified"))); /* * Actually create the target table */ intoRelationId = DefineRelation(create, relkind, InvalidOid); /* * If necessary, create a TOAST table for the target table. Note that * AlterTableCreateToastTable ends with CommandCounterIncrement(), so that * the TOAST table will be visible for insertion. */ CommandCounterIncrement(); /* parse and validate reloptions for the toast table */ toast_options = transformRelOptions((Datum) 0, create->options, "toast", validnsps, true, false); (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true); AlterTableCreateToastTable(intoRelationId, toast_options); /* Create the "view" part of a materialized view. */ if (is_matview) { /* StoreViewQuery scribbles on tree, so make a copy */ Query *query = (Query *) copyObject(into->viewQuery); StoreViewQuery(intoRelationId, query, false); CommandCounterIncrement(); } /* * Finally we can open the target table */ intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock); if (is_matview && !into->skipData) /* Make sure the heap looks good even if no rows are written. */ SetMatViewToPopulated(intoRelationDesc); /* * Check INSERT permission on the constructed table. * * XXX: It would arguably make sense to skip this check if into->skipData * is true. */ rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = intoRelationId; rte->relkind = relkind; rte->requiredPerms = ACL_INSERT; for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++) rte->modifiedCols = bms_add_member(rte->modifiedCols, attnum - FirstLowInvalidHeapAttributeNumber); ExecCheckRTPerms(list_make1(rte), true); /* * Fill private fields of myState for use by later routines */ myState->rel = intoRelationDesc; myState->output_cid = GetCurrentCommandId(true); /* * We can skip WAL-logging the insertions, unless PITR or streaming * replication is in use. We can skip the FSM in any case. */ myState->hi_options = HEAP_INSERT_SKIP_FSM | (XLogIsNeeded() ? 0 : HEAP_INSERT_SKIP_WAL); myState->bistate = GetBulkInsertState(); /* Not using WAL requires smgr_targblock be initially invalid */ Assert(RelationGetTargetBlock(intoRelationDesc) == InvalidBlockNumber); }