/*-------------------------------------------------------------------------
*
* createas.c
- * Execution of CREATE TABLE ... AS, a/k/a SELECT INTO
+ * Execution of CREATE TABLE ... AS, a/k/a SELECT INTO.
* Since CREATE MATERIALIZED VIEW shares syntax and most behaviors,
- * implement that here, too.
+ * we implement that here, too.
*
* We implement this by diverting the query's normal output to a
* specialized DestReceiver type.
*
- * Formerly, this command was implemented as a variant of SELECT, which led
+ * Formerly, CTAS was implemented as a variant of SELECT, which led
* to assorted legacy behaviors that we still try to preserve, notably that
* we must return a tuples-processed count in the completionTag.
*
#include "commands/prepare.h"
#include "commands/tablecmds.h"
#include "commands/view.h"
-#include "parser/analyze.h"
#include "parser/parse_clause.h"
#include "rewrite/rewriteHandler.h"
#include "storage/smgr.h"
{
DestReceiver pub; /* publicly-known function pointers */
IntoClause *into; /* target relation specification */
- Query *viewParse; /* the query which defines/populates data */
/* These fields are filled by intorel_startup: */
Relation rel; /* relation to write to */
CommandId output_cid; /* cmin to insert in output tuples */
static void intorel_destroy(DestReceiver *self);
-/*
- * Common setup needed by both normal execution and EXPLAIN ANALYZE.
- */
-Query *
-SetupForCreateTableAs(Query *query, IntoClause *into, const char *queryString,
- ParamListInfo params, DestReceiver *dest)
-{
- List *rewritten;
- Query *viewParse = NULL;
-
- Assert(query->commandType == CMD_SELECT);
-
- if (into->relkind == RELKIND_MATVIEW)
- viewParse = (Query *) parse_analyze((Node *) copyObject(query),
- queryString, NULL, 0)->utilityStmt;
-
- /*
- * 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);
-
- /* Save the query after rewrite but before planning. */
- ((DR_intorel *) dest)->viewParse = viewParse;
- ((DR_intorel *) dest)->into = into;
-
- if (into->relkind == RELKIND_MATVIEW)
- {
- /*
- * A materialized view would either need to save parameters for use in
- * maintaining or loading the data or prohibit them entirely. The
- * latter seems safer and more sane.
- */
- if (params != NULL && params->numParams > 0)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialized views may not be defined using bound parameters")));
- }
-
- return query;
-}
-
/*
* ExecCreateTableAs -- execute a CREATE TABLE AS command
*/
Query *query = (Query *) stmt->query;
IntoClause *into = stmt->into;
DestReceiver *dest;
+ List *rewritten;
PlannedStmt *plan;
QueryDesc *queryDesc;
ScanDirection dir;
return;
}
+ Assert(query->commandType == CMD_SELECT);
- query = SetupForCreateTableAs(query, into, queryString, params, dest);
+ /*
+ * 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);
GetIntoRelEFlags(IntoClause *intoClause)
{
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->relkind))
+ if (interpretOidsOption(intoClause->options,
+ (intoClause->viewQuery == NULL)))
flags = EXEC_FLAG_WITH_OIDS;
else
flags = EXEC_FLAG_WITHOUT_OIDS;
- Assert(intoClause->relkind != RELKIND_MATVIEW ||
- (flags & (EXEC_FLAG_WITH_OIDS | EXEC_FLAG_WITHOUT_OIDS)) ==
- EXEC_FLAG_WITHOUT_OIDS);
-
if (intoClause->skipData)
flags |= EXEC_FLAG_WITH_NO_DATA;
{
DR_intorel *myState = (DR_intorel *) self;
IntoClause *into = myState->into;
+ bool is_matview;
+ char relkind;
CreateStmt *create;
Oid intoRelationId;
Relation intoRelationDesc;
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.
if (lc != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("too many column names are specified")));
-
- /*
- * Enforce validations needed for materialized views only.
- */
- if (into->relkind == RELKIND_MATVIEW)
- {
- /*
- * Prohibit a data-modifying CTE in the query used to create a
- * materialized view. It's not sufficiently clear what the user would
- * want to happen if the MV is refreshed or incrementally maintained.
- */
- if (myState->viewParse->hasModifyingCTE)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialized views must not use data-modifying statements in WITH")));
-
- /*
- * Check whether any temporary database objects are used in the
- * creation query. It would be hard to refresh data or incrementally
- * maintain it if a source disappeared.
- */
- if (isQueryUsingTempRelation(myState->viewParse))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialized views must not use temporary tables or views")));
- }
+ errmsg("too many column names were specified")));
/*
* Actually create the target table
*/
- intoRelationId = DefineRelation(create, into->relkind, InvalidOid);
+ intoRelationId = DefineRelation(create, relkind, InvalidOid);
/*
* If necessary, create a TOAST table for the target table. Note that
AlterTableCreateToastTable(intoRelationId, toast_options);
/* Create the "view" part of a materialized view. */
- if (into->relkind == RELKIND_MATVIEW)
+ if (is_matview)
{
- StoreViewQuery(intoRelationId, myState->viewParse, false);
+ /* StoreViewQuery scribbles on tree, so make a copy */
+ Query *query = (Query *) copyObject(into->viewQuery);
+
+ StoreViewQuery(intoRelationId, query, false);
CommandCounterIncrement();
}
*/
intoRelationDesc = heap_open(intoRelationId, AccessExclusiveLock);
- if (into->relkind == RELKIND_MATVIEW && !into->skipData)
+ if (is_matview && !into->skipData)
/* Make sure the heap looks good even if no rows are written. */
SetMatViewToPopulated(intoRelationDesc);
rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RELATION;
rte->relid = intoRelationId;
- rte->relkind = into->relkind;
+ rte->relkind = relkind;
rte->isResultRel = true;
rte->requiredPerms = ACL_INSERT;
#define X_NOWHITESPACE 4
static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
- const char *queryString, DestReceiver *dest, ParamListInfo params);
+ const char *queryString, ParamListInfo params);
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
ExplainState *es);
static double elapsed_time(instr_time *starttime);
foreach(l, rewritten)
{
ExplainOneQuery((Query *) lfirst(l), NULL, &es,
- queryString, None_Receiver, params);
+ queryString, params);
/* Separate plans with an appropriate separator */
if (lnext(l) != NULL)
*/
static void
ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
- const char *queryString, DestReceiver *dest,
- ParamListInfo params)
+ const char *queryString, ParamListInfo params)
{
/* planner will not cope with utility statements */
if (query->commandType == CMD_UTILITY)
/* if an advisor plugin is present, let it manage things */
if (ExplainOneQuery_hook)
- (*ExplainOneQuery_hook) (query, into, es, queryString, dest, params);
+ (*ExplainOneQuery_hook) (query, into, es, queryString, params);
else
{
PlannedStmt *plan;
plan = pg_plan_query(query, 0, params);
/* run it (if needed) and produce output */
- ExplainOnePlan(plan, into, es, queryString, dest, params);
+ ExplainOnePlan(plan, into, es, queryString, params);
}
}
if (IsA(utilityStmt, CreateTableAsStmt))
{
- DestReceiver *dest;
-
/*
* We have to rewrite the contained SELECT and then pass it back to
* ExplainOneQuery. It's probably not really necessary to copy the
* contained parsetree another time, but let's be safe.
*/
CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
- Query *query = (Query *) ctas->query;
-
- dest = CreateIntoRelDestReceiver(into);
+ List *rewritten;
Assert(IsA(ctas->query, Query));
-
- query = SetupForCreateTableAs(query, ctas->into, queryString, params, dest);
-
- ExplainOneQuery(query, ctas->into, es, queryString, dest, params);
+ rewritten = QueryRewrite((Query *) copyObject(ctas->query));
+ Assert(list_length(rewritten) == 1);
+ ExplainOneQuery((Query *) linitial(rewritten), ctas->into, es,
+ queryString, params);
}
else if (IsA(utilityStmt, ExecuteStmt))
ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
*/
void
ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
- const char *queryString, DestReceiver *dest, ParamListInfo params)
+ const char *queryString, ParamListInfo params)
{
+ DestReceiver *dest;
QueryDesc *queryDesc;
instr_time starttime;
double totaltime = 0;
PushCopiedSnapshot(GetActiveSnapshot());
UpdateActiveSnapshotCommandId();
+ /*
+ * Normally we discard the query's output, but if explaining CREATE TABLE
+ * AS, we'd better use the appropriate tuple receiver.
+ */
+ if (into)
+ dest = CreateIntoRelDestReceiver(into);
+ else
+ dest = None_Receiver;
+
/* Create a QueryDesc for the query */
queryDesc = CreateQueryDesc(plannedstmt, queryString,
GetActiveSnapshot(), InvalidSnapshot,
PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
if (IsA(pstmt, PlannedStmt))
- ExplainOnePlan(pstmt, into, es, query_string, None_Receiver, paramLI);
+ ExplainOnePlan(pstmt, into, es, query_string, paramLI);
else
ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI);
stmt->relation = seq->sequence;
stmt->inhRelations = NIL;
stmt->constraints = NIL;
- stmt->options = list_make1(defWithOids(false));
+ stmt->options = NIL;
stmt->oncommit = ONCOMMIT_NOOP;
stmt->tablespacename = NULL;
stmt->if_not_exists = false;
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
Oid oldrelid, void *arg);
-static bool isQueryUsingTempRelation_walker(Node *node, void *context);
-
/* ----------------------------------------------------------------
* DefineRelation
*/
descriptor = BuildDescForRelation(schema);
- localHasOids = interpretOidsOption(stmt->options, relkind);
+ localHasOids = interpretOidsOption(stmt->options,
+ (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_FOREIGN_TABLE));
descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
/*
ReleaseSysCache(tuple);
}
-
-/*
- * Returns true iff any relation underlying this query is a temporary database
- * object (table, view, or materialized view).
- *
- */
-bool
-isQueryUsingTempRelation(Query *query)
-{
- return isQueryUsingTempRelation_walker((Node *) query, NULL);
-}
-
-static bool
-isQueryUsingTempRelation_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
-
- if (IsA(node, Query))
- {
- Query *query = (Query *) node;
- ListCell *rtable;
-
- foreach(rtable, query->rtable)
- {
- RangeTblEntry *rte = lfirst(rtable);
-
- if (rte->rtekind == RTE_RELATION)
- {
- Relation rel = heap_open(rte->relid, AccessShareLock);
- char relpersistence = rel->rd_rel->relpersistence;
-
- heap_close(rel, AccessShareLock);
- if (relpersistence == RELPERSISTENCE_TEMP)
- return true;
- }
- }
-
- return query_tree_walker(query,
- isQueryUsingTempRelation_walker,
- context,
- QTW_IGNORE_JOINALIASES);
- }
-
- return expression_tree_walker(node,
- isQueryUsingTempRelation_walker,
- context);
-}
createStmt->tableElts = coldeflist;
createStmt->inhRelations = NIL;
createStmt->constraints = NIL;
- createStmt->options = list_make1(defWithOids(false));
+ createStmt->options = NIL;
createStmt->oncommit = ONCOMMIT_NOOP;
createStmt->tablespacename = NULL;
createStmt->if_not_exists = false;
createStmt->inhRelations = NIL;
createStmt->constraints = NIL;
createStmt->options = options;
- createStmt->options = lappend(options, defWithOids(false));
createStmt->oncommit = ONCOMMIT_NOOP;
createStmt->tablespacename = NULL;
createStmt->if_not_exists = false;
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(onCommit);
COPY_STRING_FIELD(tableSpaceName);
+ COPY_NODE_FIELD(viewQuery);
COPY_SCALAR_FIELD(skipData);
- COPY_SCALAR_FIELD(relkind);
return newnode;
}
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(onCommit);
COMPARE_STRING_FIELD(tableSpaceName);
+ COMPARE_NODE_FIELD(viewQuery);
COMPARE_SCALAR_FIELD(skipData);
- COMPARE_SCALAR_FIELD(relkind);
return true;
}
if (walker(into->rel, context))
return true;
/* colNames, options are deemed uninteresting */
+ /* viewQuery should be null in raw parsetree, but check it */
+ if (walker(into->viewQuery, context))
+ return true;
}
break;
case T_List:
WRITE_NODE_FIELD(options);
WRITE_ENUM_FIELD(onCommit, OnCommitAction);
WRITE_STRING_FIELD(tableSpaceName);
+ WRITE_NODE_FIELD(viewQuery);
WRITE_BOOL_FIELD(skipData);
- WRITE_CHAR_FIELD(relkind);
}
static void
READ_NODE_FIELD(options);
READ_ENUM_FIELD(onCommit, OnCommitAction);
READ_STRING_FIELD(tableSpaceName);
+ READ_NODE_FIELD(viewQuery);
READ_BOOL_FIELD(skipData);
- READ_CHAR_FIELD(relkind);
READ_DONE();
}
transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
{
Query *result;
+ Query *query;
- /*
- * Set relkind in IntoClause based on statement relkind. These are
- * different types, because the parser users the ObjectType enumeration
- * and the executor uses RELKIND_* defines.
- */
- switch (stmt->relkind)
+ /* transform contained query */
+ query = transformStmt(pstate, stmt->query);
+ stmt->query = (Node *) query;
+
+ /* additional work needed for CREATE MATERIALIZED VIEW */
+ if (stmt->relkind == OBJECT_MATVIEW)
{
- case (OBJECT_TABLE):
- stmt->into->relkind = RELKIND_RELATION;
- break;
- case (OBJECT_MATVIEW):
- stmt->into->relkind = RELKIND_MATVIEW;
- break;
- default:
- elog(ERROR, "unrecognized object relkind: %d",
- (int) stmt->relkind);
- }
+ /*
+ * Prohibit a data-modifying CTE in the query used to create a
+ * materialized view. It's not sufficiently clear what the user would
+ * want to happen if the MV is refreshed or incrementally maintained.
+ */
+ if (query->hasModifyingCTE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialized views must not use data-modifying statements in WITH")));
- /* transform contained query */
- stmt->query = (Node *) transformStmt(pstate, stmt->query);
+ /*
+ * Check whether any temporary database objects are used in the
+ * creation query. It would be hard to refresh data or incrementally
+ * maintain it if a source disappeared.
+ */
+ if (isQueryUsingTempRelation(query))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialized views must not use temporary tables or views")));
+
+ /*
+ * A materialized view would either need to save parameters for use in
+ * maintaining/loading the data or prohibit them entirely. The latter
+ * seems safer and more sane.
+ */
+ if (query_contains_extern_params(query))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialized views may not be defined using bound parameters")));
+
+ /*
+ * At runtime, we'll need a copy of the parsed-but-not-rewritten Query
+ * for purposes of creating the view's ON SELECT rule. We stash that
+ * in the IntoClause because that's where intorel_startup() can
+ * conveniently get it from.
+ */
+ stmt->into->viewQuery = copyObject(query);
+ }
/* represent the command as a utility Query */
result = makeNode(Query);
#define CAS_NOT_VALID 0x10
#define CAS_NO_INHERIT 0x20
-/*
- * In the IntoClause structure there is a char value which will eventually be
- * set to RELKIND_RELATION or RELKIND_MATVIEW based on the relkind field in
- * the statement-level structure, which is an ObjectType. Define the default
- * here, which should always be overridden later.
- */
-#define INTO_CLAUSE_RELKIND_DEFAULT '\0'
#define parser_yyerror(msg) scanner_yyerror(msg, yyscanner)
#define parser_errposition(pos) scanner_errposition(pos, yyscanner)
$$->options = $3;
$$->onCommit = $4;
$$->tableSpaceName = $5;
+ $$->viewQuery = NULL;
$$->skipData = false; /* might get changed later */
- $$->relkind = INTO_CLAUSE_RELKIND_DEFAULT;
}
;
$$->options = $3;
$$->onCommit = ONCOMMIT_NOOP;
$$->tableSpaceName = $4;
+ $$->viewQuery = NULL; /* filled at analysis time */
$$->skipData = false; /* might get changed later */
- $$->relkind = INTO_CLAUSE_RELKIND_DEFAULT;
}
;
$$->options = NIL;
$$->onCommit = ONCOMMIT_NOOP;
$$->tableSpaceName = NULL;
+ $$->viewQuery = NULL;
$$->skipData = false;
- $$->relkind = INTO_CLAUSE_RELKIND_DEFAULT;
}
| /*EMPTY*/
{ $$ = NULL; }
* parsing the query string because the return value can depend upon the
* default_with_oids GUC var.
*
- * Materialized views are handled here rather than reloptions.c because that
- * code explicitly punts checking for oids to here. We prohibit any explicit
- * specification of the oids option for a materialized view, and indicate that
- * oids are not needed if we don't get an error.
+ * In some situations, we want to reject an OIDS option even if it's present.
+ * That's (rather messily) handled here rather than reloptions.c, because that
+ * code explicitly punts checking for oids to here.
*/
bool
-interpretOidsOption(List *defList, char relkind)
+interpretOidsOption(List *defList, bool allowOids)
{
ListCell *cell;
if (def->defnamespace == NULL &&
pg_strcasecmp(def->defname, "oids") == 0)
{
- if (relkind == RELKIND_MATVIEW)
+ if (!allowOids)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", "oids")));
-
+ errmsg("unrecognized parameter \"%s\"",
+ def->defname)));
return defGetBoolean(def);
}
}
- if (relkind == RELKIND_MATVIEW)
+ /* Force no-OIDS result if caller disallows OIDS. */
+ if (!allowOids)
return false;
/* OIDS option was not specified, so use default. */
Oid targetTypeId, int32 targetTypeMod,
int location);
static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
+static bool query_contains_extern_params_walker(Node *node, void *context);
/*
return expression_tree_walker(node, check_parameter_resolution_walker,
(void *) pstate);
}
+
+/*
+ * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params.
+ */
+bool
+query_contains_extern_params(Query *query)
+{
+ return query_tree_walker(query,
+ query_contains_extern_params_walker,
+ NULL, 0);
+}
+
+static bool
+query_contains_extern_params_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Param))
+ {
+ Param *param = (Param *) node;
+
+ if (param->paramkind == PARAM_EXTERN)
+ return true;
+ return false;
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ return query_tree_walker((Query *) node,
+ query_contains_extern_params_walker,
+ context, 0);
+ }
+ return expression_tree_walker(node, query_contains_extern_params_walker,
+ context);
+}
int location, bool include_dropped,
List **colnames, List **colvars);
static int specialAttNum(const char *attname);
+static bool isQueryUsingTempRelation_walker(Node *node, void *context);
/*
colname, rte->eref->aliasname) : 0,
parser_errposition(pstate, location)));
}
+
+
+/*
+ * Examine a fully-parsed query, and return TRUE iff any relation underlying
+ * the query is a temporary relation (table, view, or materialized view).
+ */
+bool
+isQueryUsingTempRelation(Query *query)
+{
+ return isQueryUsingTempRelation_walker((Node *) query, NULL);
+}
+
+static bool
+isQueryUsingTempRelation_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+
+ if (IsA(node, Query))
+ {
+ Query *query = (Query *) node;
+ ListCell *rtable;
+
+ foreach(rtable, query->rtable)
+ {
+ RangeTblEntry *rte = lfirst(rtable);
+
+ if (rte->rtekind == RTE_RELATION)
+ {
+ Relation rel = heap_open(rte->relid, AccessShareLock);
+ char relpersistence = rel->rd_rel->relpersistence;
+
+ heap_close(rel, AccessShareLock);
+ if (relpersistence == RELPERSISTENCE_TEMP)
+ return true;
+ }
+ }
+
+ return query_tree_walker(query,
+ isQueryUsingTempRelation_walker,
+ context,
+ QTW_IGNORE_JOINALIASES);
+ }
+
+ return expression_tree_walker(node,
+ isQueryUsingTempRelation_walker,
+ context);
+}
{
cxt.stmtType = "CREATE FOREIGN TABLE";
cxt.isforeign = true;
- cxt.hasoids = interpretOidsOption(stmt->options,
- RELKIND_FOREIGN_TABLE);
}
else
{
cxt.stmtType = "CREATE TABLE";
cxt.isforeign = false;
- cxt.hasoids = interpretOidsOption(stmt->options, RELKIND_RELATION);
}
cxt.relation = stmt->relation;
cxt.rel = NULL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
+ cxt.hasoids = interpretOidsOption(stmt->options, true);
Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201304071
+#define CATALOG_VERSION_NO 201304121
#endif
#include "tcop/dest.h"
-extern Query *SetupForCreateTableAs(Query *query, IntoClause *into,
- const char *queryString,
- ParamListInfo params, DestReceiver *dest);
-
extern void ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
ParamListInfo params, char *completionTag);
IntoClause *into,
ExplainState *es,
const char *queryString,
- DestReceiver *dest,
ParamListInfo params);
extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook;
const char *queryString, ParamListInfo params);
extern void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into,
- ExplainState *es, const char *queryString,
- DestReceiver *dest, ParamListInfo params);
+ ExplainState *es,
+ const char *queryString, ParamListInfo params);
extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc);
extern void RangeVarCallbackOwnsTable(const RangeVar *relation,
Oid relId, Oid oldRelId, void *arg);
-extern bool isQueryUsingTempRelation(Query *query);
-
#endif /* TABLECMDS_H */
/*
* IntoClause - target information for SELECT INTO, CREATE TABLE AS, and
* CREATE MATERIALIZED VIEW
+ *
+ * For CREATE MATERIALIZED VIEW, viewQuery is the parsed-but-not-rewritten
+ * SELECT Query for the view; otherwise it's NULL. (Although it's actually
+ * Query*, we declare it as Node* to avoid a forward reference.)
*/
typedef struct IntoClause
{
List *options; /* options from WITH clause */
OnCommitAction onCommit; /* what do we do at COMMIT? */
char *tableSpaceName; /* table space to use, or NULL */
+ Node *viewQuery; /* materialized view's SELECT query */
bool skipData; /* true for WITH NO DATA */
- char relkind; /* RELKIND_RELATION or RELKIND_MATVIEW */
} IntoClause;
extern int setTargetTable(ParseState *pstate, RangeVar *relation,
bool inh, bool alsoSource, AclMode requiredPerms);
extern bool interpretInhOption(InhOption inhOpt);
-extern bool interpretOidsOption(List *defList, char relkind);
+extern bool interpretOidsOption(List *defList, bool allowOids);
extern Node *transformWhereClause(ParseState *pstate, Node *clause,
ParseExprKind exprKind, const char *constructName);
extern void parse_variable_parameters(ParseState *pstate,
Oid **paramTypes, int *numParams);
extern void check_variable_parameters(ParseState *pstate, Query *query);
+extern bool query_contains_extern_params(Query *query);
#endif /* PARSE_PARAM_H */
extern Name attnumAttName(Relation rd, int attid);
extern Oid attnumTypeId(Relation rd, int attid);
extern Oid attnumCollationId(Relation rd, int attid);
+extern bool isQueryUsingTempRelation(Query *query);
#endif /* PARSE_RELATION_H */