1 /*-------------------------------------------------------------------------
4 * use rewrite rules to construct views
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "access/xact.h"
19 #include "catalog/namespace.h"
20 #include "commands/defrem.h"
21 #include "commands/tablecmds.h"
22 #include "commands/view.h"
23 #include "miscadmin.h"
24 #include "nodes/makefuncs.h"
25 #include "nodes/nodeFuncs.h"
26 #include "parser/analyze.h"
27 #include "parser/parse_relation.h"
28 #include "rewrite/rewriteDefine.h"
29 #include "rewrite/rewriteManip.h"
30 #include "rewrite/rewriteSupport.h"
31 #include "utils/acl.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
36 static void checkViewTupleDesc(TupleDesc newdesc
, TupleDesc olddesc
);
37 static bool isViewOnTempTable_walker(Node
*node
, void *context
);
39 /*---------------------------------------------------------------------
42 * Returns true iff any of the relations underlying this view are
44 *---------------------------------------------------------------------
47 isViewOnTempTable(Query
*viewParse
)
49 return isViewOnTempTable_walker((Node
*) viewParse
, NULL
);
53 isViewOnTempTable_walker(Node
*node
, void *context
)
60 Query
*query
= (Query
*) node
;
63 foreach(rtable
, query
->rtable
)
65 RangeTblEntry
*rte
= lfirst(rtable
);
67 if (rte
->rtekind
== RTE_RELATION
)
69 Relation rel
= heap_open(rte
->relid
, AccessShareLock
);
70 bool istemp
= rel
->rd_istemp
;
72 heap_close(rel
, AccessShareLock
);
78 return query_tree_walker(query
,
79 isViewOnTempTable_walker
,
81 QTW_IGNORE_JOINALIASES
);
84 return expression_tree_walker(node
,
85 isViewOnTempTable_walker
,
89 /*---------------------------------------------------------------------
90 * DefineVirtualRelation
92 * Create the "view" relation. `DefineRelation' does all the work,
93 * we just provide the correct arguments ... at least when we're
94 * creating a view. If we're updating an existing view, we have to
96 *---------------------------------------------------------------------
99 DefineVirtualRelation(const RangeVar
*relation
, List
*tlist
, bool replace
)
103 CreateStmt
*createStmt
= makeNode(CreateStmt
);
108 * create a list of ColumnDef nodes based on the names and types of the
109 * (non-junk) targetlist items from the view's SELECT list.
114 TargetEntry
*tle
= lfirst(t
);
118 ColumnDef
*def
= makeNode(ColumnDef
);
120 def
->colname
= pstrdup(tle
->resname
);
121 def
->typename
= makeTypeNameFromOid(exprType((Node
*) tle
->expr
),
122 exprTypmod((Node
*) tle
->expr
));
124 def
->is_local
= true;
125 def
->is_not_null
= false;
126 def
->raw_default
= NULL
;
127 def
->cooked_default
= NULL
;
128 def
->constraints
= NIL
;
130 attrList
= lappend(attrList
, def
);
136 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
137 errmsg("view must have at least one column")));
140 * Check to see if we want to replace an existing view.
142 namespaceId
= RangeVarGetCreationNamespace(relation
);
143 viewOid
= get_relname_relid(relation
->relname
, namespaceId
);
145 if (OidIsValid(viewOid
) && replace
)
148 TupleDesc descriptor
;
151 * Yes. Get exclusive lock on the existing view ...
153 rel
= relation_open(viewOid
, AccessExclusiveLock
);
156 * Make sure it *is* a view, and do permissions checks.
158 if (rel
->rd_rel
->relkind
!= RELKIND_VIEW
)
160 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
161 errmsg("\"%s\" is not a view",
162 RelationGetRelationName(rel
))));
164 if (!pg_class_ownercheck(viewOid
, GetUserId()))
165 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
166 RelationGetRelationName(rel
));
169 * Due to the namespace visibility rules for temporary objects, we
170 * should only end up replacing a temporary view with another
171 * temporary view, and vice versa.
173 Assert(relation
->istemp
== rel
->rd_istemp
);
176 * Create a tuple descriptor to compare against the existing view, and
179 descriptor
= BuildDescForRelation(attrList
);
180 checkViewTupleDesc(descriptor
, rel
->rd_att
);
183 * Seems okay, so return the OID of the pre-existing view.
185 relation_close(rel
, NoLock
); /* keep the lock! */
192 * now set the parameters for keys/inheritance etc. All of these are
193 * uninteresting for views...
195 createStmt
->relation
= (RangeVar
*) relation
;
196 createStmt
->tableElts
= attrList
;
197 createStmt
->inhRelations
= NIL
;
198 createStmt
->constraints
= NIL
;
199 createStmt
->options
= list_make1(defWithOids(false));
200 createStmt
->oncommit
= ONCOMMIT_NOOP
;
201 createStmt
->tablespacename
= NULL
;
204 * finally create the relation (this will error out if there's an
205 * existing view, so we don't need more code to complain if "replace"
208 return DefineRelation(createStmt
, RELKIND_VIEW
);
213 * Verify that tupledesc associated with proposed new view definition
214 * matches tupledesc of old view. This is basically a cut-down version
215 * of equalTupleDescs(), with code added to generate specific complaints.
218 checkViewTupleDesc(TupleDesc newdesc
, TupleDesc olddesc
)
222 if (newdesc
->natts
!= olddesc
->natts
)
224 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
225 errmsg("cannot change number of columns in view")));
226 /* we can ignore tdhasoid */
228 for (i
= 0; i
< newdesc
->natts
; i
++)
230 Form_pg_attribute newattr
= newdesc
->attrs
[i
];
231 Form_pg_attribute oldattr
= olddesc
->attrs
[i
];
233 /* XXX not right, but we don't support DROP COL on view anyway */
234 if (newattr
->attisdropped
!= oldattr
->attisdropped
)
236 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
237 errmsg("cannot change number of columns in view")));
239 if (strcmp(NameStr(newattr
->attname
), NameStr(oldattr
->attname
)) != 0)
241 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
242 errmsg("cannot change name of view column \"%s\"",
243 NameStr(oldattr
->attname
))));
244 /* XXX would it be safe to allow atttypmod to change? Not sure */
245 if (newattr
->atttypid
!= oldattr
->atttypid
||
246 newattr
->atttypmod
!= oldattr
->atttypmod
)
248 (errcode(ERRCODE_INVALID_TABLE_DEFINITION
),
249 errmsg("cannot change data type of view column \"%s\"",
250 NameStr(oldattr
->attname
))));
251 /* We can ignore the remaining attributes of an attribute... */
255 * We ignore the constraint fields. The new view desc can't have any
256 * constraints, and the only ones that could be on the old view are
257 * defaults, which we are happy to leave in place.
262 DefineViewRules(Oid viewOid
, Query
*viewParse
, bool replace
)
265 * Set up the ON SELECT rule. Since the query has already been through
266 * parse analysis, we use DefineQueryRewrite() directly.
268 DefineQueryRewrite(pstrdup(ViewSelectRuleName
),
274 list_make1(viewParse
));
277 * Someday: automatic ON INSERT, etc
281 /*---------------------------------------------------------------
282 * UpdateRangeTableOfViewParse
284 * Update the range table of the given parsetree.
285 * This update consists of adding two new entries IN THE BEGINNING
286 * of the range table (otherwise the rule system will die a slow,
287 * horrible and painful death, and we do not want that now, do we?)
288 * one for the OLD relation and one for the NEW one (both of
289 * them refer in fact to the "view" relation).
291 * Of course we must also increase the 'varnos' of all the Var nodes
294 * These extra RT entries are not actually used in the query,
295 * except for run-time permission checking.
296 *---------------------------------------------------------------
299 UpdateRangeTableOfViewParse(Oid viewOid
, Query
*viewParse
)
303 RangeTblEntry
*rt_entry1
,
307 * Make a copy of the given parsetree. It's not so much that we don't
308 * want to scribble on our input, it's that the parser has a bad habit of
309 * outputting multiple links to the same subtree for constructs like
310 * BETWEEN, and we mustn't have OffsetVarNodes increment the varno of a
311 * Var node twice. copyObject will expand any multiply-referenced subtree
312 * into multiple copies.
314 viewParse
= (Query
*) copyObject(viewParse
);
316 /* need to open the rel for addRangeTableEntryForRelation */
317 viewRel
= relation_open(viewOid
, AccessShareLock
);
320 * Create the 2 new range table entries and form the new range table...
321 * OLD first, then NEW....
323 rt_entry1
= addRangeTableEntryForRelation(NULL
, viewRel
,
324 makeAlias("*OLD*", NIL
),
326 rt_entry2
= addRangeTableEntryForRelation(NULL
, viewRel
,
327 makeAlias("*NEW*", NIL
),
329 /* Must override addRangeTableEntry's default access-check flags */
330 rt_entry1
->requiredPerms
= 0;
331 rt_entry2
->requiredPerms
= 0;
333 new_rt
= lcons(rt_entry1
, lcons(rt_entry2
, viewParse
->rtable
));
335 viewParse
->rtable
= new_rt
;
338 * Now offset all var nodes by 2, and jointree RT indexes too.
340 OffsetVarNodes((Node
*) viewParse
, 2, 0);
342 relation_close(viewRel
, AccessShareLock
);
349 * Execute a CREATE VIEW command.
352 DefineView(ViewStmt
*stmt
, const char *queryString
)
359 * Run parse analysis to convert the raw parse tree to a Query. Note this
360 * also acquires sufficient locks on the source table(s).
362 * Since parse analysis scribbles on its input, copy the raw parse tree;
363 * this ensures we don't corrupt a prepared statement, for example.
365 viewParse
= parse_analyze((Node
*) copyObject(stmt
->query
),
366 queryString
, NULL
, 0);
369 * The grammar should ensure that the result is a single SELECT Query.
371 if (!IsA(viewParse
, Query
) ||
372 viewParse
->commandType
!= CMD_SELECT
)
373 elog(ERROR
, "unexpected parse analysis result");
376 * If a list of column names was given, run through and insert these into
377 * the actual query tree. - thomas 2000-03-08
379 if (stmt
->aliases
!= NIL
)
381 ListCell
*alist_item
= list_head(stmt
->aliases
);
382 ListCell
*targetList
;
384 foreach(targetList
, viewParse
->targetList
)
386 TargetEntry
*te
= (TargetEntry
*) lfirst(targetList
);
388 Assert(IsA(te
, TargetEntry
));
389 /* junk columns don't get aliases */
392 te
->resname
= pstrdup(strVal(lfirst(alist_item
)));
393 alist_item
= lnext(alist_item
);
394 if (alist_item
== NULL
)
395 break; /* done assigning aliases */
398 if (alist_item
!= NULL
)
400 (errcode(ERRCODE_SYNTAX_ERROR
),
401 errmsg("CREATE VIEW specifies more column "
402 "names than columns")));
406 * If the user didn't explicitly ask for a temporary view, check whether
407 * we need one implicitly. We allow TEMP to be inserted automatically as
408 * long as the CREATE command is consistent with that --- no explicit
412 if (!view
->istemp
&& isViewOnTempTable(viewParse
))
414 view
= copyObject(view
); /* don't corrupt original command */
417 (errmsg("view \"%s\" will be a temporary view",
422 * Create the view relation
424 * NOTE: if it already exists and replace is false, the xact will be
427 viewOid
= DefineVirtualRelation(view
, viewParse
->targetList
,
431 * The relation we have just created is not visible to any other commands
432 * running with the same transaction & command id. So, increment the
433 * command id counter (but do NOT pfree any memory!!!!)
435 CommandCounterIncrement();
438 * The range table of 'viewParse' does not contain entries for the "OLD"
439 * and "NEW" relations. So... add them!
441 viewParse
= UpdateRangeTableOfViewParse(viewOid
, viewParse
);
444 * Now create the rules associated with the view.
446 DefineViewRules(viewOid
, viewParse
, stmt
->replace
);