#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);
}
1.7.1