diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index 5a853c4..3336061 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -36,6 +36,8 @@ #include "commands/tablecmds.h" #include "commands/view.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "parser/parse_clause.h" #include "rewrite/rewriteHandler.h" #include "storage/smgr.h" @@ -65,6 +67,126 @@ static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinf static bool intorel_receive(TupleTableSlot *slot, DestReceiver *self); static void intorel_shutdown(DestReceiver *self); static void intorel_destroy(DestReceiver *self); +static void CreateMatView(Query *query, IntoClause *into); + +/* + * CreateMatView + * + * Create materialized view using the rewritten query tree and an INTO clause + * defined via CREATE TABLE AS. + * XXX: rework that. + */ +static void +CreateMatView(Query *query, IntoClause *into) +{ + CreateStmt *createStmt = makeNode(CreateStmt); + List *tlist = query->targetList; + List *attrList; + ListCell *t, *lc; + Datum toast_options; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + + /* Build list of attributes */ + attrList = NIL; + lc = list_head(into->colNames); + foreach(t, tlist) + { + TargetEntry *tle = lfirst(t); + + if (!tle->resjunk) + { + ColumnDef *def = makeNode(ColumnDef); + + if (lc) + { + def->colname = strVal(lfirst(lc)); + lc = lnext(lc); + } + else + def->colname = pstrdup(tle->resname); + + def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr), + exprTypmod((Node *) tle->expr)); + def->inhcount = 0; + def->is_local = true; + def->is_not_null = false; + def->is_from_type = false; + def->storage = 0; + def->raw_default = NULL; + def->cooked_default = NULL; + def->collClause = NULL; + def->collOid = exprCollation((Node *) tle->expr); + def->location = -1; + + /* + * It's possible that the column is of a collatable type but the + * collation could not be resolved, so double-check. + */ + if (type_is_collatable(exprType((Node *) tle->expr))) + { + if (!OidIsValid(def->collOid)) + ereport(ERROR, + (errcode(ERRCODE_INDETERMINATE_COLLATION), + errmsg("could not determine which collation to use for view column \"%s\"", + def->colname), + errhint("Use the COLLATE clause to set the collation explicitly."))); + } + else + Assert(!OidIsValid(def->collOid)); + def->constraints = NIL; + + attrList = lappend(attrList, def); + } + } + + if (lc != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("too many column names were specified"))); + + /* + * Options for materialized views. + */ + createStmt->relation = into->rel; + createStmt->tableElts = attrList; + createStmt->inhRelations = NIL; + createStmt->constraints = NIL; + createStmt->options = into->options; + createStmt->oncommit = into->onCommit; + createStmt->tablespacename = into->tableSpaceName; + createStmt->if_not_exists = false; + + /* + * finally create the relation (this will error out if there's an + * existing view, so we don't need more code to complain if "replace" + * is false). + */ + CreateAsReladdr = DefineRelation(createStmt, RELKIND_MATVIEW, InvalidOid, NULL); + + /* + * If necessary, create a TOAST table for the target table. Note that + * NewRelationCreateToastTable 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, + createStmt->options, + "toast", + validnsps, + true, false); + + (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true); + + NewRelationCreateToastTable(CreateAsReladdr.objectId, toast_options); + + /* Create the "view" part of a materialized view. */ + StoreViewQuery(CreateAsReladdr.objectId, + (Query *) copyObject(into->viewQuery), + false); + CommandCounterIncrement(); +} /* @@ -161,6 +283,17 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString, query = (Query *) linitial(rewritten); Assert(query->commandType == CMD_SELECT); + /* + * For a materialized view with no data defined, there is no need to + * go through the parser and the executor. Just define the relation + * with everything it needs and leave. + */ + if (is_matview && into->skipData) + { + CreateMatView(query, into); + goto finish; + } + /* plan the query */ plan = pg_plan_query(query, 0, params); @@ -207,6 +340,8 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString, PopActiveSnapshot(); +finish: + if (is_matview) { /* Roll back any GUC changes */