postgresql/src/backend/commands/view.c

316 lines
7.8 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* view.c--
* use rewrite rules to construct views
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: view.c,v 1.31 1999/02/03 21:16:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdio.h>
#include <string.h>
1996-11-06 09:21:43 +01:00
#include <postgres.h>
#include <catalog/heap.h>
#include <access/heapam.h>
#include <access/xact.h>
#include <utils/builtins.h>
#include <nodes/relation.h>
#include <parser/parse_relation.h>
#include <parser/parse_type.h>
1996-11-06 09:21:43 +01:00
#include <rewrite/rewriteDefine.h>
#include <rewrite/rewriteHandler.h>
#include <rewrite/rewriteManip.h>
#include <rewrite/rewriteRemove.h>
#include <commands/creatinh.h>
#include <commands/view.h>
/*---------------------------------------------------------------------
* DefineVirtualRelation
*
* Create the "view" relation.
* `DefineRelation' does all the work, we just provide the correct
* arguments!
*
* If the relation already exists, then 'DefineRelation' will abort
* the xact...
*---------------------------------------------------------------------
*/
static void
DefineVirtualRelation(char *relname, List *tlist)
{
CreateStmt createStmt;
List *attrList,
*t;
TargetEntry *entry;
Resdom *res;
char *resname;
char *restypename;
/*
* create a list with one entry per attribute of this relation. Each
* entry is a two element list. The first element is the name of the
* attribute (a string) and the second the name of the type (NOTE: a
* string, not a type id!).
*/
attrList = NIL;
if (tlist != NIL)
{
foreach(t, tlist)
{
ColumnDef *def = makeNode(ColumnDef);
TypeName *typename;
/*
* find the names of the attribute & its type
*/
entry = lfirst(t);
res = entry->resdom;
resname = res->resname;
restypename = typeidTypeName(res->restype);
typename = makeNode(TypeName);
typename->name = pstrdup(restypename);
typename->typmod = res->restypmod;
def->colname = pstrdup(resname);
def->typename = typename;
def->is_not_null = false;
def->defval = (char *) NULL;
attrList = lappend(attrList, def);
}
}
else
elog(ERROR, "attempted to define virtual relation with no attrs");
/*
* now create the parametesr for keys/inheritance etc. All of them are
* nil...
*/
createStmt.relname = relname;
createStmt.istemp = false;
createStmt.tableElts = attrList;
/* createStmt.tableType = NULL;*/
createStmt.inhRelnames = NIL;
createStmt.constraints = NIL;
/*
* finally create the relation...
*/
DefineRelation(&createStmt, RELKIND_RELATION);
}
/*------------------------------------------------------------------
* makeViewRetrieveRuleName
*
* Given a view name, returns the name for the 'on retrieve to "view"'
* rule.
* This routine is called when defining/removing a view.
*------------------------------------------------------------------
*/
char *
MakeRetrieveViewRuleName(char *viewName)
{
char *buf;
buf = palloc(strlen(viewName) + 5);
snprintf(buf, strlen(viewName) + 5, "_RET%s", viewName);
return buf;
}
static RuleStmt *
FormViewRetrieveRule(char *viewName, Query *viewParse)
{
RuleStmt *rule;
char *rname;
Attr *attr;
/*
* Create a RuleStmt that corresponds to the suitable rewrite rule
* args for DefineQueryRewrite();
*/
rule = makeNode(RuleStmt);
rname = MakeRetrieveViewRuleName(viewName);
attr = makeNode(Attr);
attr->relname = pstrdup(viewName);
/* attr->refname = pstrdup(viewName);*/
rule->rulename = pstrdup(rname);
rule->whereClause = NULL;
rule->event = CMD_SELECT;
rule->object = attr;
rule->instead = true;
rule->actions = lcons(viewParse, NIL);
return rule;
}
static void
DefineViewRules(char *viewName, Query *viewParse)
{
RuleStmt *retrieve_rule = NULL;
#ifdef NOTYET
RuleStmt *replace_rule = NULL;
RuleStmt *append_rule = NULL;
RuleStmt *delete_rule = NULL;
#endif
retrieve_rule = FormViewRetrieveRule(viewName, viewParse);
#ifdef NOTYET
replace_rule = FormViewReplaceRule(viewName, viewParse);
append_rule = FormViewAppendRule(viewName, viewParse);
delete_rule = FormViewDeleteRule(viewName, viewParse);
#endif
DefineQueryRewrite(retrieve_rule);
#ifdef NOTYET
DefineQueryRewrite(replace_rule);
DefineQueryRewrite(append_rule);
DefineQueryRewrite(delete_rule);
#endif
}
/*---------------------------------------------------------------
* UpdateRangeTableOfViewParse
*
* Update the range table of the given parsetree.
* This update consists of adding two new entries IN THE BEGINNING
* of the range table (otherwise the rule system will die a slow,
* horrible and painful death, and we do not want that now, do we?)
* one for the CURRENT relation and one for the NEW one (both of
* them refer in fact to the "view" relation).
*
* Of course we must also increase the 'varnos' of all the Var nodes
* by 2...
*
* NOTE: these are destructive changes. It would be difficult to
* make a complete copy of the parse tree and make the changes
* in the copy.
*---------------------------------------------------------------
*/
static void
UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
{
List *old_rt;
List *new_rt;
RangeTblEntry *rt_entry1,
*rt_entry2;
/*
* first offset all var nodes by 2
*/
OffsetVarNodes((Node *) viewParse->targetList, 2, 0);
OffsetVarNodes(viewParse->qual, 2, 0);
OffsetVarNodes(viewParse->havingQual, 2, 0);
/*
* find the old range table...
*/
old_rt = viewParse->rtable;
/*
* create the 2 new range table entries and form the new range
* table... CURRENT first, then NEW....
*/
rt_entry1 = addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*",
FALSE, FALSE);
rt_entry2 = addRangeTableEntry(NULL, (char *) viewName, "*NEW*",
FALSE, FALSE);
new_rt = lcons(rt_entry2, old_rt);
new_rt = lcons(rt_entry1, new_rt);
/*
* Now the tricky part.... Update the range table in place... Be
* careful here, or hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
*/
viewParse->rtable = new_rt;
}
/*-------------------------------------------------------------------
* DefineView
*
* - takes a "viewname", "parsetree" pair and then
* 1) construct the "virtual" relation
* 2) commit the command but NOT the transaction,
* so that the relation exists
* before the rules are defined.
* 2) define the "n" rules specified in the PRS2 paper
* over the "virtual" relation
*-------------------------------------------------------------------
*/
void
DefineView(char *viewName, Query *viewParse)
{
List *viewTlist;
viewTlist = viewParse->targetList;
/*
* Create the "view" relation NOTE: if it already exists, the xaxt
* will be aborted.
*/
DefineVirtualRelation(viewName, viewTlist);
/*
* The relation we have just created is not visible to any other
* commands running with the same transaction & command id. So,
* increment the command id counter (but do NOT pfree any memory!!!!)
*/
CommandCounterIncrement();
/*
* The range table of 'viewParse' does not contain entries for the
* "CURRENT" and "NEW" relations. So... add them! NOTE: we make the
* update in place! After this call 'viewParse' will never be what it
* used to be...
*/
UpdateRangeTableOfViewParse(viewName, viewParse);
DefineViewRules(viewName, viewParse);
}
/*------------------------------------------------------------------
* RemoveView
*
* Remove a view given its name
*------------------------------------------------------------------
*/
void
RemoveView(char *viewName)
{
char *rname;
/*
* first remove all the "view" rules... Currently we only have one!
*/
rname = MakeRetrieveViewRuleName(viewName);
RemoveRewriteRule(rname);
/*
* we don't really need that, but just in case...
*/
CommandCounterIncrement();
/*
* now remove the relation.
*/
heap_destroy_with_catalog(viewName);
pfree(rname);
}