Commit f93b6974 authored by Bruce Momjian's avatar Bruce Momjian

Here's a combination of all the patches I'm currently waiting

    for against a just updated CVS tree. It contains

        Partial new rewrite system that handles subselects,  view
        aggregate  columns, insert into select from view, updates
        with set col = view-value and select rules restriction to
        view definition.

        Updates  for  rule/view  backparsing utility functions to
        handle subselects correct.


        New system views pg_tables and pg_indexes (where you  can
        see the complete index definition in the latter one).

        Enabling array references on query parameters.

        Bugfix for functional index.

        Little changes to system views pg_rules and pg_views.


    The rule system isn't a release-stopper any longer.

    But  another  stopper  is  that  I  don't  know if the latest
    changes to PL/pgSQL (not already in CVS) made it  compile  on
    AIX. Still wait for some response from Dave.

Jan
parent 9b21a18c
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.27 1998/09/07 05:35:30 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.28 1998/10/02 16:27:43 momjian Exp $
*
* INTERFACE ROUTINES
* index_open - open an index relation by relationId
......@@ -362,7 +362,7 @@ GetIndexValue(HeapTuple tuple,
bool *attNull)
{
Datum returnVal;
bool isNull;
bool isNull = FALSE;
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid)
{
......@@ -375,13 +375,15 @@ GetIndexValue(HeapTuple tuple,
attrNums[i],
hTupDesc,
attNull);
if (*attNull)
isNull = TRUE;
}
returnVal = (Datum) fmgr_array_args(FIgetProcOid(fInfo),
FIgetnArgs(fInfo),
(char **) attData,
&isNull);
pfree(attData);
*attNull = FALSE;
*attNull = isNull;
}
else
returnVal = heap_getattr(tuple, attrNums[attOff], hTupDesc, attNull);
......
......@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.13 1998/09/01 04:31:30 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.14 1998/10/02 16:27:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -24,7 +24,6 @@
#include "utils/builtins.h"
#include "catalog/pg_shadow.h"
static void checkLockPerms(List *locks, Query *parsetree, int rt_index);
/*
* ThisLockWasTriggered
......@@ -170,7 +169,7 @@ matchLocks(CmdType event,
}
static void
void
checkLockPerms(List *locks, Query *parsetree, int rt_index)
{
Relation ev_rel;
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.21 1998/09/01 04:31:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.22 1998/10/02 16:27:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -199,11 +199,8 @@ DefineQueryRewrite(RuleStmt *stmt)
/* ----------
* The current rewrite handler is known to work on relation level
* rules only. And for SELECT events, it expects one non-nothing
* action that is instead. Since we now hand out views and rules
* to regular users, we must deny anything else.
*
* I know that I must write a new rewrite handler from scratch
* for 6.5 so we can remove these checks and allow all the rules.
* action that is instead and returns exactly a tuple of the
* rewritten relation. This restricts SELECT rules to views.
*
* Jan
* ----------
......@@ -217,6 +214,9 @@ DefineQueryRewrite(RuleStmt *stmt)
else
eslot_string = NULL;
/*
* No rule actions that modify OLD or NEW
*/
if (action != NIL)
foreach(l, action)
{
......@@ -233,23 +233,86 @@ DefineQueryRewrite(RuleStmt *stmt)
}
}
/*
* Rules ON SELECT are restricted to view definitions
*/
if (event_type == CMD_SELECT)
{
TargetEntry *tle;
Resdom *resdom;
Form_pg_attribute attr;
char *attname;
int i;
/*
* So there cannot be INSTEAD NOTHING, ...
*/
if (length(action) == 0)
{
elog(NOTICE, "instead nothing rules on select currently not supported");
elog(ERROR, " use views instead");
}
/*
* ... there cannot be multiple actions, ...
*/
if (length(action) > 1)
elog(ERROR, "multiple action rules on select currently not supported");
/*
* ... the one action must be a SELECT, ...
*/
query = (Query *) lfirst(action);
if (!is_instead || query->commandType != CMD_SELECT)
elog(ERROR, "only instead-select rules currently supported on select");
if (event_qual != NULL)
elog(ERROR, "event qualifications not supported for rules on select");
/*
* ... the targetlist of the SELECT action must
* exactly match the event relation ...
*/
event_relation = heap_openr(event_obj->relname);
if (event_relation == NULL)
elog(ERROR, "virtual relations not supported yet");
if (event_relation->rd_att->natts != length(query->targetList))
elog(ERROR, "select rules target list must match event relations structure");
for (i = 1; i <= event_relation->rd_att->natts; i++) {
tle = (TargetEntry *)nth(i - 1, query->targetList);
resdom = tle->resdom;
attr = event_relation->rd_att->attrs[i - 1];
attname = nameout(&(attr->attname));
if (strcmp(resdom->resname, attname) != 0)
elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
if (attr->atttypid != resdom->restype)
elog(ERROR, "select rules target entry %d has different type from attribute %s", i, attname);
if (attr->atttypmod != resdom->restypmod)
elog(ERROR, "select rules target entry %d has different size from attribute %s", i, attname);
}
/*
* ... and final there must not be another ON SELECT
* rule already.
*/
if (event_relation->rd_rules != NULL) {
for (i = 0; i < event_relation->rd_rules->numLocks; i++) {
RewriteRule *rule;
rule = event_relation->rd_rules->rules[i];
if (rule->event == CMD_SELECT)
elog(ERROR, "%s is already a view", nameout(&(event_relation->rd_rel->relname)));
}
}
heap_close(event_relation);
}
/*
* This rule is currently allowed - too restricted I know - but women
* and children first Jan
* This rule is allowed - install it.
*/
event_relation = heap_openr(event_obj->relname);
......
......@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.21 1998/09/01 04:31:33 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.22 1998/10/02 16:27:47 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -18,6 +18,7 @@
#include "utils/rel.h"
#include "nodes/pg_list.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "parser/parsetree.h" /* for parsetree manipulation */
#include "parser/parse_relation.h"
......@@ -31,20 +32,44 @@
#include "commands/creatinh.h"
#include "access/heapam.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/acl.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_type.h"
static RewriteInfo *gatherRewriteMeta(Query *parsetree,
Query *rule_action,
Node *rule_qual,
int rt_index,
CmdType event,
bool *instead_flag);
static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
static bool attribute_used(Node *node, int rt_index, int attno, int sublevels_up);
static void offset_varnodes(Node *node, int offset, int sublevels_up);
static void change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up);
static void modifyAggregUplevel(Node *node);
static void modifyAggregChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up);
static void modifyAggregDropQual(Node **nodePtr, Node *orignode, Expr *expr);
static SubLink *modifyAggregMakeSublink(Expr *origexp, Query *parsetree);
static void modifyAggregQual(Node **nodePtr, Query *parsetree);
static Query *fireRIRrules(Query *parsetree);
static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule,
int rt_index, int relation_level,
Relation relation, int *modified);
static List *fireRules(Query *parsetree, int rt_index, CmdType event,
bool *instead_flag, List *locks, List **qual_products);
static void QueryRewriteSubLink(Node *node);
static List *QueryRewriteOne(Query *parsetree);
static List *deepRewriteQuery(Query *parsetree);
static void RewritePreprocessQuery(Query *parsetree);
static Query *RewritePostprocessNonSelect(Query *parsetree);
/*
* gatherRewriteMeta -
......@@ -118,316 +143,2083 @@ gatherRewriteMeta(Query *parsetree,
return info;
}
static List *
OptimizeRIRRules(List *locks)
{
List *attr_level = NIL,
*i;
List *relation_level = NIL;
foreach(i, locks)
{
RewriteRule *rule_lock = lfirst(i);
if (rule_lock->attrno == -1)
relation_level = lappend(relation_level, rule_lock);
else
attr_level = lappend(attr_level, rule_lock);
}
return nconc(relation_level, attr_level);
}
/*
* idea is to fire regular rules first, then qualified instead
* rules and unqualified instead rules last. Any lemming is counted for.
* rangeTableEntry_used -
* we need to process a RTE for RIR rules only if it is
* referenced somewhere in var nodes of the query.
*/
static List *
orderRules(List *locks)
static bool
rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
{
List *regular = NIL;
List *instead_rules = NIL;
List *instead_qualified = NIL;
List *i;
if (node == NULL)
return FALSE;
foreach(i, locks)
{
RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
if (rule_lock->isInstead)
{
if (rule_lock->qual == NULL)
instead_rules = lappend(instead_rules, rule_lock);
else
instead_qualified = lappend(instead_qualified, rule_lock);
}
else
regular = lappend(regular, rule_lock);
}
regular = nconc(regular, instead_qualified);
return nconc(regular, instead_rules);
}
return rangeTableEntry_used(
(Node *)(tle->expr),
rt_index,
sublevels_up);
}
break;
static int
AllRetrieve(List *actions)
{
List *n;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
foreach(n, actions)
{
Query *pt = lfirst(n);
return rangeTableEntry_used(
(Node *)(agg->target),
rt_index,
sublevels_up);
}
break;
/*
* in the old postgres code, we check whether command_type is a
* consp of '('*'.commandType). but we've never supported
* transitive closures. Hence removed - ay 10/94.
*/
if (pt->commandType != CMD_SELECT)
return false;
}
return true;
}
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
static List *
FireRetrieveRulesAtQuery(Query *parsetree,
int rt_index,
Relation relation,
bool *instead_flag,
int rule_flag)
{
List *i,
*locks;
RuleLock *rt_entry_locks = NULL;
List *work = NIL;
return rangeTableEntry_used(
(Node *)(grp->entry),
rt_index,
sublevels_up);
}
break;
if ((rt_entry_locks = relation->rd_rules) == NULL)
return NIL;
case T_Expr:
{
Expr *exp = (Expr *)node;
return rangeTableEntry_used(
(Node *)(exp->args),
rt_index,
sublevels_up);
}
break;
locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
case T_Iter:
{
Iter *iter = (Iter *)node;
/* find all retrieve instead */
foreach(i, locks)
{
RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
return rangeTableEntry_used(
(Node *)(iter->iterexpr),
rt_index,
sublevels_up);
}
break;
if (!rule_lock->isInstead)
continue;
work = lappend(work, rule_lock);
}
if (work != NIL)
{
work = OptimizeRIRRules(locks);
foreach(i, work)
{
RewriteRule *rule_lock = lfirst(i);
int relation_level;
int modified = FALSE;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
if (rangeTableEntry_used(
(Node *)(ref->refupperindexpr),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *)(ref->reflowerindexpr),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *)(ref->refexpr),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *)(ref->refassgnexpr),
rt_index,
sublevels_up))
return TRUE;
return FALSE;
}
break;
case T_Var:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up)
return var->varno == rt_index;
else
return FALSE;
}
break;
case T_Param:
return FALSE;
relation_level = (rule_lock->attrno == -1);
if (rule_lock->actions == NIL)
case T_Const:
return FALSE;
case T_List:
{
*instead_flag = TRUE;
return NIL;
List *l;
foreach (l, (List *)node) {
if (rangeTableEntry_used(
(Node *)lfirst(l),
rt_index,
sublevels_up))
return TRUE;
}
return FALSE;
}
if (!rule_flag &&
length(rule_lock->actions) >= 2 &&
AllRetrieve(rule_lock->actions))
break;
case T_SubLink:
{
*instead_flag = TRUE;
return rule_lock->actions;
SubLink *sub = (SubLink *)node;
if (rangeTableEntry_used(
(Node *)(sub->lefthand),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *)(sub->subselect),
rt_index,
sublevels_up + 1))
return TRUE;
return FALSE;
}
ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
&modified);
if (modified)
break;
case T_Query:
{
*instead_flag = TRUE;
FixResdomTypes(parsetree->targetList);
return lcons(parsetree, NIL);
Query *qry = (Query *)node;
if (rangeTableEntry_used(
(Node *)(qry->targetList),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *)(qry->qual),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *)(qry->havingQual),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *)(qry->groupClause),
rt_index,
sublevels_up))
return TRUE;
return FALSE;
}
}
break;
default:
elog(NOTICE, "unknown node tag %d in rangeTableEntry_used()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
return NIL;
return FALSE;
}
/* Idea is like this:
*
* retrieve-instead-retrieve rules have different semantics than update nodes
* Separate RIR rules from others. Pass others to FireRules.
* Order RIR rules and process.
*
* side effect: parsetree's rtable field might be changed
/*
* attribute_used -
* Check if a specific attribute number of a RTE is used
* somewhere in the query
*/
static void
ApplyRetrieveRule(Query *parsetree,
RewriteRule *rule,
int rt_index,
int relation_level,
Relation relation,
int *modified)
static bool
attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
{
Query *rule_action = NULL;
Node *rule_qual;
List *rtable,
*rt;
int nothing,
rt_length;
int badsql = FALSE;
if (node == NULL)
return FALSE;
rule_qual = rule->qual;
if (rule->actions)
{
if (length(rule->actions) > 1) /* ??? because we don't handle
* rules with more than one
* action? -ay */
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
/*
* WARNING!!! If we sometimes handle rules with more than one
* action, the view acl checks might get broken.
* viewAclOverride should only become true (below) if this is
* a relation_level, instead, select query - Jan
*/
return;
rule_action = copyObject(lfirst(rule->actions));
nothing = FALSE;
}
else
nothing = TRUE;
return attribute_used(
(Node *)(tle->expr),
rt_index,
attno,
sublevels_up);
}
break;
rtable = copyObject(parsetree->rtable);
foreach(rt, rtable)
{
RangeTblEntry *rte = lfirst(rt);
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
/*
* this is to prevent add_missing_vars_to_base_rels() from adding
* a bogus entry to the new target list.
*/
rte->inFromCl = false;
}
rt_length = length(rtable);
return attribute_used(
(Node *)(agg->target),
rt_index,
attno,
sublevels_up);
}
break;
rtable = nconc(rtable, copyObject(rule_action->rtable));
parsetree->rtable = rtable;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
rule_action->rtable = rtable;
OffsetVarNodes(rule_action->qual, rt_length);
OffsetVarNodes((Node *) rule_action->targetList, rt_length);
OffsetVarNodes(rule_qual, rt_length);
return attribute_used(
(Node *)(grp->entry),
rt_index,
attno,
sublevels_up);
}
break;
OffsetVarNodes((Node *) rule_action->groupClause, rt_length);
OffsetVarNodes((Node *) rule_action->havingQual, rt_length);
case T_Expr:
{
Expr *exp = (Expr *)node;
ChangeVarNodes(rule_action->qual,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
ChangeVarNodes((Node *) rule_action->targetList,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
return attribute_used(
(Node *)(exp->args),
rt_index,
attno,
sublevels_up);
}
break;
ChangeVarNodes((Node *) rule_action->groupClause,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
ChangeVarNodes((Node *) rule_action->havingQual,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
case T_Iter:
{
Iter *iter = (Iter *)node;
if (relation_level)
{
HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
modified);
}
else
{
HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
rt_index, rule->attrno, modified, &badsql);
}
if (*modified && !badsql)
{
AddQual(parsetree, rule_action->qual);
return attribute_used(
(Node *)(iter->iterexpr),
rt_index,
attno,
sublevels_up);
}
break;
/*
* This will only work if the query made to the view defined by
* the following groupClause groups by the same attributes or does
* not use group at all!
*/
if (parsetree->groupClause == NULL)
parsetree->groupClause = rule_action->groupClause;
AddHavingQual(parsetree, rule_action->havingQual);
parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
}
}
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
if (attribute_used(
(Node *)(ref->refupperindexpr),
rt_index,
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *)(ref->reflowerindexpr),
rt_index,
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *)(ref->refexpr),
rt_index,
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *)(ref->refassgnexpr),
rt_index,
attno,
sublevels_up))
return TRUE;
return FALSE;
}
break;
static List *
ProcessRetrieveQuery(Query *parsetree,
List *rtable,
bool *instead_flag,
bool rule)
{
List *rt;
List *product_queries = NIL;
int rt_index = 0;
case T_Var:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up)
return var->varno == rt_index;
else
return FALSE;
}
break;
foreach(rt, rtable)
{
RangeTblEntry *rt_entry = lfirst(rt);
Relation rt_entry_relation = NULL;
List *result = NIL;
case T_Param:
return FALSE;
rt_index++;
rt_entry_relation = heap_openr(rt_entry->relname);
case T_Const:
return FALSE;
case T_List:
{
List *l;
foreach (l, (List *)node) {
if (attribute_used(
(Node *)lfirst(l),
rt_index,
attno,
sublevels_up))
return TRUE;
}
return FALSE;
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
if (attribute_used(
(Node *)(sub->lefthand),
rt_index,
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *)(sub->subselect),
rt_index,
attno,
sublevels_up + 1))
return TRUE;
return FALSE;
}
break;
case T_Query:
{
Query *qry = (Query *)node;
if (attribute_used(
(Node *)(qry->targetList),
rt_index,
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *)(qry->qual),
rt_index,
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *)(qry->havingQual),
rt_index,
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *)(qry->groupClause),
rt_index,
attno,
sublevels_up))
return TRUE;
return FALSE;
}
break;
default:
elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
if (rt_entry_relation->rd_rules != NULL)
{
result =
FireRetrieveRulesAtQuery(parsetree,
rt_index,
rt_entry_relation,
instead_flag,
rule);
}
heap_close(rt_entry_relation);
if (*instead_flag)
return result;
}
if (rule)
return NIL;
rt_index = 0;
return FALSE;
}
foreach(rt, rtable)
{
RangeTblEntry *rt_entry = lfirst(rt);
Relation rt_entry_relation = NULL;
RuleLock *rt_entry_locks = NULL;
List *result = NIL;
List *locks = NIL;
List *dummy_products;
rt_index++;
rt_entry_relation = heap_openr(rt_entry->relname);
rt_entry_locks = rt_entry_relation->rd_rules;
heap_close(rt_entry_relation);
/*
* offset_varnodes -
* We need another version of OffsetVarNodes() when processing
* RIR rules
*/
static void
offset_varnodes(Node *node, int offset, int sublevels_up)
{
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
offset_varnodes(
(Node *)(tle->expr),
offset,
sublevels_up);
}
break;
if (rt_entry_locks)
{
locks =
matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
}
if (locks != NIL)
{
result = fireRules(parsetree, rt_index, CMD_SELECT,
instead_flag, locks, &dummy_products);
if (*instead_flag)
return lappend(NIL, result);
if (result != NIL)
product_queries = nconc(product_queries, result);
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
offset_varnodes(
(Node *)(agg->target),
offset,
sublevels_up);
}
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
offset_varnodes(
(Node *)(grp->entry),
offset,
sublevels_up);
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
offset_varnodes(
(Node *)(exp->args),
offset,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
offset_varnodes(
(Node *)(iter->iterexpr),
offset,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
offset_varnodes(
(Node *)(ref->refupperindexpr),
offset,
sublevels_up);
offset_varnodes(
(Node *)(ref->reflowerindexpr),
offset,
sublevels_up);
offset_varnodes(
(Node *)(ref->refexpr),
offset,
sublevels_up);
offset_varnodes(
(Node *)(ref->refassgnexpr),
offset,
sublevels_up);
}
break;
case T_Var:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up) {
var->varno += offset;
var->varnoold += offset;
}
}
break;
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach (l, (List *)node)
offset_varnodes(
(Node *)lfirst(l),
offset,
sublevels_up);
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
offset_varnodes(
(Node *)(sub->lefthand),
offset,
sublevels_up);
offset_varnodes(
(Node *)(sub->subselect),
offset,
sublevels_up + 1);
}
break;
case T_Query:
{
Query *qry = (Query *)node;
offset_varnodes(
(Node *)(qry->targetList),
offset,
sublevels_up);
offset_varnodes(
(Node *)(qry->qual),
offset,
sublevels_up);
offset_varnodes(
(Node *)(qry->havingQual),
offset,
sublevels_up);
offset_varnodes(
(Node *)(qry->groupClause),
offset,
sublevels_up);
}
break;
default:
elog(NOTICE, "unknown node tag %d in offset_varnodes()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
/*
* change_varnodes -
* and another ChangeVarNodes() too
*/
static void
change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up)
{
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
change_varnodes(
(Node *)(tle->expr),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
change_varnodes(
(Node *)(agg->target),
rt_index,
new_index,
sublevels_up);
}
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
change_varnodes(
(Node *)(grp->entry),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
change_varnodes(
(Node *)(exp->args),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
change_varnodes(
(Node *)(iter->iterexpr),
rt_index,
new_index,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
change_varnodes(
(Node *)(ref->refupperindexpr),
rt_index,
new_index,
sublevels_up);
change_varnodes(
(Node *)(ref->reflowerindexpr),
rt_index,
new_index,
sublevels_up);
change_varnodes(
(Node *)(ref->refexpr),
rt_index,
new_index,
sublevels_up);
change_varnodes(
(Node *)(ref->refassgnexpr),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Var:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up &&
var->varno == rt_index) {
var->varno = new_index;
var->varnoold = new_index;
}
}
break;
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach (l, (List *)node)
change_varnodes(
(Node *)lfirst(l),
rt_index,
new_index,
sublevels_up);
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
change_varnodes(
(Node *)(sub->lefthand),
rt_index,
new_index,
sublevels_up);
change_varnodes(
(Node *)(sub->subselect),
rt_index,
new_index,
sublevels_up + 1);
}
break;
case T_Query:
{
Query *qry = (Query *)node;
change_varnodes(
(Node *)(qry->targetList),
rt_index,
new_index,
sublevels_up);
change_varnodes(
(Node *)(qry->qual),
rt_index,
new_index,
sublevels_up);
change_varnodes(
(Node *)(qry->havingQual),
rt_index,
new_index,
sublevels_up);
change_varnodes(
(Node *)(qry->groupClause),
rt_index,
new_index,
sublevels_up);
}
break;
default:
elog(NOTICE, "unknown node tag %d in change_varnodes()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
/*
* modifyAggregUplevel -
* In the newly created sublink for an aggregate column used in
* the qualification, we must adjust the varlevelsup in all the
* var nodes.
*/
static void
modifyAggregUplevel(Node *node)
{
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
modifyAggregUplevel(
(Node *)(tle->expr));
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
modifyAggregUplevel(
(Node *)(agg->target));
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
modifyAggregUplevel(
(Node *)(exp->args));
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
modifyAggregUplevel(
(Node *)(iter->iterexpr));
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
modifyAggregUplevel(
(Node *)(ref->refupperindexpr));
modifyAggregUplevel(
(Node *)(ref->reflowerindexpr));
modifyAggregUplevel(
(Node *)(ref->refexpr));
modifyAggregUplevel(
(Node *)(ref->refassgnexpr));
}
break;
case T_Var:
{
Var *var = (Var *)node;
var->varlevelsup++;
}
break;
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach (l, (List *)node)
modifyAggregUplevel(
(Node *)lfirst(l));
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
modifyAggregUplevel(
(Node *)(sub->lefthand));
modifyAggregUplevel(
(Node *)(sub->oper));
modifyAggregUplevel(
(Node *)(sub->subselect));
}
break;
case T_Query:
{
Query *qry = (Query *)node;
modifyAggregUplevel(
(Node *)(qry->targetList));
modifyAggregUplevel(
(Node *)(qry->qual));
modifyAggregUplevel(
(Node *)(qry->havingQual));
modifyAggregUplevel(
(Node *)(qry->groupClause));
}
break;
default:
elog(NOTICE, "unknown node tag %d in modifyAggregUplevel()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
/*
* modifyAggregChangeVarnodes -
* Change the var nodes in a sublink created for an aggregate column
* used in the qualification that is subject of the aggregate
* function to point to the correct local RTE.
*/
static void
modifyAggregChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up)
{
Node *node = *nodePtr;
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
modifyAggregChangeVarnodes(
(Node **)(&(tle->expr)),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
modifyAggregChangeVarnodes(
(Node **)(&(agg->target)),
rt_index,
new_index,
sublevels_up);
}
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
modifyAggregChangeVarnodes(
(Node **)(&(grp->entry)),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
modifyAggregChangeVarnodes(
(Node **)(&(exp->args)),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
modifyAggregChangeVarnodes(
(Node **)(&(iter->iterexpr)),
rt_index,
new_index,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
modifyAggregChangeVarnodes(
(Node **)(&(ref->refupperindexpr)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(ref->reflowerindexpr)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(ref->refexpr)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(ref->refassgnexpr)),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Var:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up &&
var->varno == rt_index) {
var = copyObject(var);
var->varno = new_index;
var->varnoold = new_index;
var->varlevelsup = 0;
*nodePtr = (Node *)var;
}
}
break;
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach (l, (List *)node)
modifyAggregChangeVarnodes(
(Node **)(&lfirst(l)),
rt_index,
new_index,
sublevels_up);
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
modifyAggregChangeVarnodes(
(Node **)(&(sub->lefthand)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(sub->oper)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(sub->subselect)),
rt_index,
new_index,
sublevels_up + 1);
}
break;
case T_Query:
{
Query *qry = (Query *)node;
modifyAggregChangeVarnodes(
(Node **)(&(qry->targetList)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(qry->qual)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(qry->havingQual)),
rt_index,
new_index,
sublevels_up);
modifyAggregChangeVarnodes(
(Node **)(&(qry->groupClause)),
rt_index,
new_index,
sublevels_up);
}
break;
default:
elog(NOTICE, "unknown node tag %d in modifyAggregChangeVarnodes()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
/*
* modifyAggregDropQual -
* remove the pure aggreg clase from a qualification
*/
static void
modifyAggregDropQual(Node **nodePtr, Node *orignode, Expr *expr)
{
Node *node = *nodePtr;
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_Var:
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
Aggreg *oagg = (Aggreg *)orignode;
modifyAggregDropQual(
(Node **)(&(agg->target)),
(Node *)(oagg->target),
expr);
}
break;
case T_Param:
break;
case T_Const:
break;
case T_GroupClause:
break;
case T_Expr:
{
Expr *this_expr = (Expr *)node;
Expr *orig_expr = (Expr *)orignode;
if (orig_expr == expr) {
Const *ctrue;
if (expr->typeOid != BOOLOID)
elog(ERROR,
"aggregate expression in qualification isn't of type bool");
ctrue = makeNode(Const);
ctrue->consttype = BOOLOID;
ctrue->constlen = 1;
ctrue->constisnull = FALSE;
ctrue->constvalue = (Datum)TRUE;
ctrue->constbyval = TRUE;
*nodePtr = (Node *)ctrue;
}
else
modifyAggregDropQual(
(Node **)(&(this_expr->args)),
(Node *)(orig_expr->args),
expr);
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
Iter *oiter = (Iter *)orignode;
modifyAggregDropQual(
(Node **)(&(iter->iterexpr)),
(Node *)(oiter->iterexpr),
expr);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
ArrayRef *oref = (ArrayRef *)orignode;
modifyAggregDropQual(
(Node **)(&(ref->refupperindexpr)),
(Node *)(oref->refupperindexpr),
expr);
modifyAggregDropQual(
(Node **)(&(ref->reflowerindexpr)),
(Node *)(oref->reflowerindexpr),
expr);
modifyAggregDropQual(
(Node **)(&(ref->refexpr)),
(Node *)(oref->refexpr),
expr);
modifyAggregDropQual(
(Node **)(&(ref->refassgnexpr)),
(Node *)(oref->refassgnexpr),
expr);
}
break;
case T_List:
{
List *l;
List *ol = (List *)orignode;
int li = 0;
foreach (l, (List *)node) {
modifyAggregDropQual(
(Node **)(&(lfirst(l))),
(Node *)nth(li, ol),
expr);
li++;
}
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
SubLink *osub = (SubLink *)orignode;
modifyAggregDropQual(
(Node **)(&(sub->subselect)),
(Node *)(osub->subselect),
expr);
}
break;
case T_Query:
{
Query *qry = (Query *)node;
Query *oqry = (Query *)orignode;
modifyAggregDropQual(
(Node **)(&(qry->qual)),
(Node *)(oqry->qual),
expr);
modifyAggregDropQual(
(Node **)(&(qry->havingQual)),
(Node *)(oqry->havingQual),
expr);
}
break;
default:
elog(NOTICE, "unknown node tag %d in modifyAggregDropQual()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
/*
* modifyAggregMakeSublink -
* Create a sublink node for a qualification expression that
* uses an aggregate column of a view
*/
static SubLink *
modifyAggregMakeSublink(Expr *origexp, Query *parsetree)
{
SubLink *sublink;
Query *subquery;
Node *subqual;
RangeTblEntry *rte;
Aggreg *aggreg;
Var *target;
TargetEntry *tle;
Resdom *resdom;
Expr *exp = copyObject(origexp);
if (nodeTag(nth(0, exp->args)) == T_Aggreg)
if (nodeTag(nth(1, exp->args)) == T_Aggreg)
elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported");
else
elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual");
aggreg = (Aggreg *)nth(1, exp->args);
target = (Var *)(aggreg->target);
rte = (RangeTblEntry *)nth(target->varno - 1, parsetree->rtable);
tle = makeNode(TargetEntry);
resdom = makeNode(Resdom);
aggreg->usenulls = TRUE;
resdom->resno = 1;
resdom->restype = ((Oper *)(exp->oper))->opresulttype;
resdom->restypmod = -1;
resdom->resname = pstrdup("<noname>");
resdom->reskey = 0;
resdom->reskeyop = 0;
resdom->resjunk = 0;
tle->resdom = resdom;
tle->expr = (Node *)aggreg;
subqual = copyObject(parsetree->qual);
modifyAggregDropQual((Node **)&subqual, (Node *)parsetree->qual, origexp);
sublink = makeNode(SubLink);
sublink->subLinkType = EXPR_SUBLINK;
sublink->useor = FALSE;
sublink->lefthand = lappend(NIL, copyObject(lfirst(exp->args)));
sublink->oper = lappend(NIL, copyObject(exp));
sublink->subselect = NULL;
subquery = makeNode(Query);
sublink->subselect = (Node *)subquery;
subquery->commandType = CMD_SELECT;
subquery->utilityStmt = NULL;
subquery->resultRelation = 0;
subquery->into = NULL;
subquery->isPortal = FALSE;
subquery->isBinary = FALSE;
subquery->unionall = FALSE;
subquery->uniqueFlag = NULL;
subquery->sortClause = NULL;
subquery->rtable = lappend(NIL, rte);
subquery->targetList = lappend(NIL, tle);
subquery->qual = subqual;
subquery->groupClause = NIL;
subquery->havingQual = NULL;
subquery->hasAggs = TRUE;
subquery->hasSubLinks = FALSE;
subquery->unionClause = NULL;
modifyAggregUplevel((Node *)sublink);
modifyAggregChangeVarnodes((Node **)&(sublink->lefthand), target->varno,
1, target->varlevelsup);
modifyAggregChangeVarnodes((Node **)&(sublink->oper), target->varno,
1, target->varlevelsup);
modifyAggregChangeVarnodes((Node **)&(sublink->subselect), target->varno,
1, target->varlevelsup);
return sublink;
}
/*
* modifyAggregQual -
* Search for qualification expressions that contain aggregate
* functions and substiture them by sublinks. These expressions
* originally come from qualifications that use aggregate columns
* of a view.
*/
static void
modifyAggregQual(Node **nodePtr, Query *parsetree)
{
Node *node = *nodePtr;
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_Var:
break;
case T_Param:
break;
case T_Const:
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
modifyAggregQual(
(Node **)(&(grp->entry)),
parsetree);
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
SubLink *sub;
if (length(exp->args) != 2) {
modifyAggregQual(
(Node **)(&(exp->args)),
parsetree);
break;
}
if (nodeTag(nth(0, exp->args)) != T_Aggreg &&
nodeTag(nth(1, exp->args)) != T_Aggreg) {
modifyAggregQual(
(Node **)(&(exp->args)),
parsetree);
break;
}
sub = modifyAggregMakeSublink(exp,
parsetree);
*nodePtr = (Node *)sub;
parsetree->hasSubLinks = TRUE;
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
modifyAggregQual(
(Node **)(&(iter->iterexpr)),
parsetree);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
modifyAggregQual(
(Node **)(&(ref->refupperindexpr)),
parsetree);
modifyAggregQual(
(Node **)(&(ref->reflowerindexpr)),
parsetree);
modifyAggregQual(
(Node **)(&(ref->refexpr)),
parsetree);
modifyAggregQual(
(Node **)(&(ref->refassgnexpr)),
parsetree);
}
break;
case T_List:
{
List *l;
foreach (l, (List *)node)
modifyAggregQual(
(Node **)(&(lfirst(l))),
parsetree);
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
modifyAggregQual(
(Node **)(&(sub->subselect)),
(Query *)(sub->subselect));
}
break;
case T_Query:
{
Query *qry = (Query *)node;
modifyAggregQual(
(Node **)(&(qry->qual)),
parsetree);
modifyAggregQual(
(Node **)(&(qry->havingQual)),
parsetree);
}
break;
default:
elog(NOTICE, "unknown node tag %d in modifyAggregQual()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
static Node *
FindMatchingTLEntry(List *tlist, char *e_attname)
{
List *i;
foreach(i, tlist)
{
TargetEntry *tle = lfirst(i);
char *resname;
resname = tle->resdom->resname;
if (!strcmp(e_attname, resname))
return (tle->expr);
}
return NULL;
}
static Node *
make_null(Oid type)
{
Const *c = makeNode(Const);
c->consttype = type;
c->constlen = get_typlen(type);
c->constvalue = PointerGetDatum(NULL);
c->constisnull = true;
c->constbyval = get_typbyval(type);
return (Node *) c;
}
static void
apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, int *modified, int sublevels_up)
{
Node *node = *nodePtr;
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
apply_RIR_view(
(Node **)(&(tle->expr)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
apply_RIR_view(
(Node **)(&(agg->target)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
apply_RIR_view(
(Node **)(&(grp->entry)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
apply_RIR_view(
(Node **)(&(exp->args)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
apply_RIR_view(
(Node **)(&(iter->iterexpr)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
apply_RIR_view(
(Node **)(&(ref->refupperindexpr)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
apply_RIR_view(
(Node **)(&(ref->reflowerindexpr)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
apply_RIR_view(
(Node **)(&(ref->refexpr)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
apply_RIR_view(
(Node **)(&(ref->refassgnexpr)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_Var:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up &&
var->varno == rt_index) {
Node *exp;
exp = FindMatchingTLEntry(
tlist,
get_attname(rte->relid,
var->varattno));
if (exp == NULL) {
*nodePtr = make_null(var->vartype);
return;
}
if (var->varlevelsup > 0 &&
nodeTag(exp) == T_Var) {
exp = copyObject(exp);
((Var *)exp)->varlevelsup = var->varlevelsup;
}
*nodePtr = exp;
*modified = TRUE;
}
}
break;
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach (l, (List *)node)
apply_RIR_view(
(Node **)(&(lfirst(l))),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
apply_RIR_view(
(Node **)(&(sub->lefthand)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
apply_RIR_view(
(Node **)(&(sub->subselect)),
rt_index,
rte,
tlist,
modified,
sublevels_up + 1);
}
break;
case T_Query:
{
Query *qry = (Query *)node;
apply_RIR_view(
(Node **)(&(qry->targetList)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
apply_RIR_view(
(Node **)(&(qry->qual)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
apply_RIR_view(
(Node **)(&(qry->havingQual)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
apply_RIR_view(
(Node **)(&(qry->groupClause)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
default:
elog(NOTICE, "unknown node tag %d in apply_RIR_view()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
static void
ApplyRetrieveRule(Query *parsetree,
RewriteRule *rule,
int rt_index,
int relation_level,
Relation relation,
int *modified)
{
Query *rule_action = NULL;
Node *rule_qual;
List *rtable,
*rt;
int nothing,
rt_length;
int badsql = FALSE;
rule_qual = rule->qual;
if (rule->actions)
{
if (length(rule->actions) > 1) /* ??? because we don't handle
* rules with more than one
* action? -ay */
return;
rule_action = copyObject(lfirst(rule->actions));
nothing = FALSE;
}
else
nothing = TRUE;
rtable = copyObject(parsetree->rtable);
foreach(rt, rtable)
{
RangeTblEntry *rte = lfirst(rt);
/*
* this is to prevent add_missing_vars_to_base_rels() from adding
* a bogus entry to the new target list.
*/
rte->inFromCl = false;
}
rt_length = length(rtable);
rtable = nconc(rtable, copyObject(rule_action->rtable));
parsetree->rtable = rtable;
rule_action->rtable = rtable;
offset_varnodes((Node *) rule_qual, rt_length, 0);
offset_varnodes((Node *) rule_action, rt_length, 0);
change_varnodes((Node *) rule_qual,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
change_varnodes((Node *) rule_action,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
if (relation_level)
{
apply_RIR_view((Node **) &parsetree, rt_index,
(RangeTblEntry *)nth(rt_index - 1, rtable),
rule_action->targetList, modified, 0);
apply_RIR_view((Node **) &rule_action, rt_index,
(RangeTblEntry *)nth(rt_index - 1, rtable),
rule_action->targetList, modified, 0);
}
else
{
HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
rt_index, rule->attrno, modified, &badsql);
}
if (*modified && !badsql) {
AddQual(parsetree, rule_action->qual);
/* This will only work if the query made to the view defined by the following
* groupClause groups by the same attributes or does not use group at all! */
if (parsetree->groupClause == NULL)
parsetree->groupClause=rule_action->groupClause;
AddHavingQual(parsetree, rule_action->havingQual);
parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
}
}
static void
fireRIRonSubselect(Node *node)
{
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
fireRIRonSubselect(
(Node *)(tle->expr));
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
fireRIRonSubselect(
(Node *)(agg->target));
}
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
fireRIRonSubselect(
(Node *)(grp->entry));
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
fireRIRonSubselect(
(Node *)(exp->args));
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
fireRIRonSubselect(
(Node *)(iter->iterexpr));
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
fireRIRonSubselect(
(Node *)(ref->refupperindexpr));
fireRIRonSubselect(
(Node *)(ref->reflowerindexpr));
fireRIRonSubselect(
(Node *)(ref->refexpr));
fireRIRonSubselect(
(Node *)(ref->refassgnexpr));
}
break;
case T_Var:
break;
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach (l, (List *)node)
fireRIRonSubselect(
(Node *)(lfirst(l)));
}
break;
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
Query *qry;
fireRIRonSubselect(
(Node *)(sub->lefthand));
qry = fireRIRrules((Query *)(sub->subselect));
fireRIRonSubselect(
(Node *)qry);
sub->subselect = (Node *) qry;
}
break;
case T_Query:
{
Query *qry = (Query *)node;
fireRIRonSubselect(
(Node *)(qry->targetList));
fireRIRonSubselect(
(Node *)(qry->qual));
fireRIRonSubselect(
(Node *)(qry->havingQual));
fireRIRonSubselect(
(Node *)(qry->groupClause));
}
break;
default:
elog(NOTICE, "unknown node tag %d in fireRIRonSubselect()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
}
/*
* fireRIRrules -
* Apply all RIR rules on each rangetable entry in a query
*/
static Query *
fireRIRrules(Query *parsetree)
{
int rt_index;
RangeTblEntry *rte;
Relation rel;
List *locks;
RuleLock *rules;
RewriteRule *rule;
RewriteRule RIRonly;
int modified;
int i;
List *l;
rt_index = 0;
while(rt_index < length(parsetree->rtable)) {
++rt_index;
if (!rangeTableEntry_used((Node *)parsetree, rt_index, 0))
continue;
rte = nth(rt_index - 1, parsetree->rtable);
rel = heap_openr(rte->relname);
if (rel->rd_rules == NULL) {
heap_close(rel);
continue;
}
rules = rel->rd_rules;
locks = NIL;
/*
* Collect the RIR rules that we must apply
*/
for (i = 0; i < rules->numLocks; i++) {
rule = rules->rules[i];
if (rule->event != CMD_SELECT)
continue;
if (rule->attrno > 0 &&
!attribute_used((Node *)parsetree,
rt_index,
rule->attrno, 0))
continue;
locks = lappend(locks, rule);
}
/*
* Check permissions
*/
checkLockPerms(locks, parsetree, rt_index);
/*
* Now apply them
*/
foreach (l, locks) {
rule = lfirst(l);
RIRonly.event = rule->event;
RIRonly.attrno = rule->attrno;
RIRonly.qual = rule->qual;
RIRonly.actions = rule->actions;
ApplyRetrieveRule(parsetree,
&RIRonly,
rt_index,
RIRonly.attrno == -1,
rel,
&modified);
}
heap_close(rel);
}
fireRIRonSubselect((Node *) parsetree);
modifyAggregQual((Node **) &(parsetree->qual), parsetree);
return parsetree;
}
/*
* idea is to fire regular rules first, then qualified instead
* rules and unqualified instead rules last. Any lemming is counted for.
*/
static List *
orderRules(List *locks)
{
List *regular = NIL;
List *instead_rules = NIL;
List *instead_qualified = NIL;
List *i;
foreach(i, locks)
{
RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
if (rule_lock->isInstead)
{
if (rule_lock->qual == NULL)
instead_rules = lappend(instead_rules, rule_lock);
else
instead_qualified = lappend(instead_qualified, rule_lock);
}
else
regular = lappend(regular, rule_lock);
}
return product_queries;
regular = nconc(regular, instead_qualified);
return nconc(regular, instead_rules);
}
static Query *
CopyAndAddQual(Query *parsetree,
List *actions,
......@@ -462,6 +2254,7 @@ CopyAndAddQual(Query *parsetree,
}
/*
* fireRules -
* Iterate through rule locks applying rules.
......@@ -488,13 +2281,7 @@ fireRules(Query *parsetree,
/* choose rule to fire from list of rules */
if (locks == NIL)
{
ProcessRetrieveQuery(parsetree,
parsetree->rtable,
instead_flag, TRUE);
if (*instead_flag)
return lappend(NIL, parsetree);
else
return NIL;
return NIL;
}
locks = orderRules(locks); /* real instead rules last */
......@@ -505,7 +2292,6 @@ fireRules(Query *parsetree,
*event_qual;
List *actions;
List *r;
bool orig_instead_flag = *instead_flag;
/*
* Instead rules change the resultRelation of the query. So the
......@@ -645,8 +2431,10 @@ fireRules(Query *parsetree,
*--------------------------------------------------
*/
info->rule_action->rtable = info->rt;
/*
ProcessRetrieveQuery(info->rule_action, info->rt,
&orig_instead_flag, TRUE);
*/
/*--------------------------------------------------
* Step 4
......@@ -670,128 +2458,32 @@ fireRules(Query *parsetree,
return results;
}
/* ----------
* RewritePreprocessQuery -
* adjust details in the parsetree, the rule system
* depends on
* ----------
*/
static void
RewritePreprocessQuery(Query *parsetree)
{
/* ----------
* if the query has a resultRelation, reassign the
* result domain numbers to the attribute numbers in the
* target relation. FixNew() depends on it when replacing
* *new* references in a rule action by the expressions
* from the rewritten query.
* ----------
*/
if (parsetree->resultRelation > 0)
{
RangeTblEntry *rte;
Relation rd;
List *tl;
TargetEntry *tle;
int resdomno;
rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
parsetree->rtable);
rd = heap_openr(rte->relname);
foreach(tl, parsetree->targetList)
{
tle = (TargetEntry *) lfirst(tl);
resdomno = attnameAttNum(rd, tle->resdom->resname);
tle->resdom->resno = resdomno;
}
heap_close(rd);
}
}
/* ----------
* RewritePostprocessNonSelect -
* apply instead select rules on a query fired in by
* the rewrite system
* ----------
*/
static Query *
RewritePostprocessNonSelect(Query *parsetree)
{
List *rt;
int rt_index = 0;
Query *newtree = copyObject(parsetree);
foreach(rt, parsetree->rtable)
{
RangeTblEntry *rt_entry = lfirst(rt);
Relation rt_entry_relation = NULL;
RuleLock *rt_entry_locks = NULL;
List *locks = NIL;
List *instead_locks = NIL;
List *lock;
RewriteRule *rule;
rt_index++;
rt_entry_relation = heap_openr(rt_entry->relname);
rt_entry_locks = rt_entry_relation->rd_rules;
if (rt_entry_locks)
{
int origcmdtype = newtree->commandType;
newtree->commandType = CMD_SELECT;
locks =
matchLocks(CMD_SELECT, rt_entry_locks, rt_index, newtree);
newtree->commandType = origcmdtype;
}
if (locks != NIL)
{
foreach(lock, locks)
{
rule = (RewriteRule *) lfirst(lock);
if (rule->isInstead)
instead_locks = nconc(instead_locks, lock);
}
}
if (instead_locks != NIL)
{
foreach(lock, instead_locks)
{
int relation_level;
int modified = 0;
rule = (RewriteRule *) lfirst(lock);
relation_level = (rule->attrno == -1);
ApplyRetrieveRule(newtree,
rule,
rt_index,
relation_level,
rt_entry_relation,
&modified);
}
}
heap_close(rt_entry_relation);
}
return newtree;
}
static List *
RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
{
CmdType event;
List *product_queries = NIL;
int result_relation = 0;
List *product_queries = NIL;
int result_relation = 0;
RangeTblEntry *rt_entry;
Relation rt_entry_relation = NULL;
RuleLock *rt_entry_locks = NULL;
Assert(parsetree != NULL);
event = parsetree->commandType;
/*
* SELECT rules are handled later when we have all the
* queries that should get executed
*/
if (event == CMD_SELECT)
return NIL;
/*
* Utilities aren't rewritten at all - why is this here?
*/
if (event == CMD_UTILITY)
return NIL;
......@@ -803,79 +2495,34 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
result_relation = parsetree->resultRelation;
if (event != CMD_SELECT)
{
/*
* the statement is an update, insert or delete
*/
RangeTblEntry *rt_entry;
Relation rt_entry_relation = NULL;
RuleLock *rt_entry_locks = NULL;
rt_entry = rt_fetch(result_relation, parsetree->rtable);
rt_entry_relation = heap_openr(rt_entry->relname);
rt_entry_locks = rt_entry_relation->rd_rules;
heap_close(rt_entry_relation);
if (rt_entry_locks != NULL)
{
List *locks =
matchLocks(event, rt_entry_locks, result_relation, parsetree);
product_queries =
fireRules(parsetree,
result_relation,
event,
instead_flag,
locks,
qual_products);
}
/* ----------
* deepRewriteQuery does not handle the situation
* where a query fired by a rule uses relations that
* have instead select rules defined (views and the like).
* So we care for them here.
* ----------
*/
if (product_queries != NIL)
{
List *pq;
Query *tmp;
List *new_products = NIL;
foreach(pq, product_queries)
{
tmp = (Query *) lfirst(pq);
tmp = RewritePostprocessNonSelect(tmp);
new_products = lappend(new_products, tmp);
}
product_queries = new_products;
}
/*
* the statement is an update, insert or delete - fire rules
* on it.
*/
rt_entry = rt_fetch(result_relation, parsetree->rtable);
rt_entry_relation = heap_openr(rt_entry->relname);
rt_entry_locks = rt_entry_relation->rd_rules;
heap_close(rt_entry_relation);
return product_queries;
}
else
if (rt_entry_locks != NULL)
{
List *locks =
matchLocks(event, rt_entry_locks, result_relation, parsetree);
product_queries =
fireRules(parsetree,
result_relation,
event,
instead_flag,
locks,
qual_products);
}
/*
* the statement is a select
*/
Query *other;
/*
* ApplyRetrieveRule changes the range table XXX Unions are copied
* again.
*/
other = copyObject(parsetree);
return product_queries;
return
ProcessRetrieveQuery(other, parsetree->rtable,
instead_flag, FALSE);
}
}
/*
* to avoid infinite recursion, we restrict the number of times a query
* can be rewritten. Detecting cycles is left for the reader as an excercise.
......@@ -886,103 +2533,6 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
static int numQueryRewriteInvoked = 0;
/*
* QueryRewrite -
* rewrite one query via QueryRewrite system, possibly returning 0, or many
* queries
*/
List *
QueryRewrite(Query *parsetree)
{
RewritePreprocessQuery(parsetree);
QueryRewriteSubLink(parsetree->qual);
QueryRewriteSubLink(parsetree->havingQual);
return QueryRewriteOne(parsetree);
}
/*
* QueryRewriteSubLink
*
* This rewrites the SubLink subqueries first, doing the lowest ones first.
* We already have code in the main rewrite loops to process correlated
* variables from upper queries that exist in subqueries.
*/
static void
QueryRewriteSubLink(Node *node)
{
if (node == NULL)
return;
switch (nodeTag(node))
{
case T_TargetEntry:
break;
case T_Aggreg:
break;
case T_Expr:
{
Expr *expr = (Expr *) node;
QueryRewriteSubLink((Node *) expr->args);
}
break;
case T_Var:
break;
case T_List:
{
List *l;
foreach(l, (List *) node)
QueryRewriteSubLink(lfirst(l));
}
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
List *ret;
/*
* Nest down first. We do this so if a rewrite adds a
* SubLink we don't process it as part of this loop.
*/
QueryRewriteSubLink((Node *) query->qual);
QueryRewriteSubLink((Node *) query->havingQual);
ret = QueryRewriteOne(query);
if (!ret)
sublink->subselect = NULL;
else if (lnext(ret) == NIL)
sublink->subselect = lfirst(ret);
else
elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries.");
}
break;
default:
/* ignore the others */
break;
}
return;
}
/*
* QueryOneRewrite -
* rewrite one query
*/
static List *
QueryRewriteOne(Query *parsetree)
{
numQueryRewriteInvoked = 0;
/*
* take a deep breath and apply all the rewrite rules - ay
*/
return deepRewriteQuery(parsetree);
}
/*
* deepRewriteQuery -
* rewrites the query and apply the rules again on the queries rewritten
......@@ -1040,3 +2590,104 @@ deepRewriteQuery(Query *parsetree)
return rewritten;
}
/*
* QueryOneRewrite -
* rewrite one query
*/
static List *
QueryRewriteOne(Query *parsetree)
{
numQueryRewriteInvoked = 0;
/*
* take a deep breath and apply all the rewrite rules - ay
*/
return deepRewriteQuery(parsetree);
}
/* ----------
* RewritePreprocessQuery -
* adjust details in the parsetree, the rule system
* depends on
* ----------
*/
static void
RewritePreprocessQuery(Query *parsetree)
{
/* ----------
* if the query has a resultRelation, reassign the
* result domain numbers to the attribute numbers in the
* target relation. FixNew() depends on it when replacing
* *new* references in a rule action by the expressions
* from the rewritten query.
* ----------
*/
if (parsetree->resultRelation > 0)
{
RangeTblEntry *rte;
Relation rd;
List *tl;
TargetEntry *tle;
int resdomno;
rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
parsetree->rtable);
rd = heap_openr(rte->relname);
foreach(tl, parsetree->targetList)
{
tle = (TargetEntry *) lfirst(tl);
resdomno = attnameAttNum(rd, tle->resdom->resname);
tle->resdom->resno = resdomno;
}
heap_close(rd);
}
}
/*
* QueryRewrite -
* rewrite one query via query rewrite system, possibly returning 0
* or many queries
*/
List *
QueryRewrite(Query *parsetree)
{
List *querylist;
List *results = NIL;
List *l;
Query *query;
/*
* Step 1
*
* There still seems something broken with the resdom numbers
* so we reassign them first.
*/
RewritePreprocessQuery(parsetree);
/*
* Step 2
*
* Apply all non-SELECT rules possibly getting 0 or many queries
*/
querylist = QueryRewriteOne(parsetree);
/*
* Step 3
*
* Apply all the RIR rules on each query
*/
foreach (l, querylist) {
query = (Query *)lfirst(l);
results = lappend(results, fireRIRrules(query));
}
return results;
}
......@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.18 1998/09/11 16:39:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.19 1998/10/02 16:27:49 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -73,6 +73,23 @@ OffsetVarNodes(Node *node, int offset)
OffsetVarNodes((Node *) expr->args, offset);
}
break;
case T_Iter:
{
Iter *iter = (Iter *) node;
OffsetVarNodes((Node *) iter->iterexpr, offset);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *) node;
OffsetVarNodes((Node *) ref->refupperindexpr, offset);
OffsetVarNodes((Node *) ref->reflowerindexpr, offset);
OffsetVarNodes((Node *) ref->refexpr, offset);
OffsetVarNodes((Node *) ref->refassgnexpr, offset);
}
break;
case T_Var:
{
Var *var = (Var *) node;
......@@ -157,6 +174,23 @@ ChangeVarNodes(Node *node, int old_varno, int new_varno, int sublevels_up)
ChangeVarNodes((Node *) expr->args, old_varno, new_varno, sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *) node;
ChangeVarNodes((Node *) iter->iterexpr, old_varno, new_varno, sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *) node;
ChangeVarNodes((Node *) ref->refupperindexpr, old_varno, new_varno, sublevels_up);
ChangeVarNodes((Node *) ref->reflowerindexpr, old_varno, new_varno, sublevels_up);
ChangeVarNodes((Node *) ref->refexpr, old_varno, new_varno, sublevels_up);
ChangeVarNodes((Node *) ref->refassgnexpr, old_varno, new_varno, sublevels_up);
}
break;
case T_Var:
{
Var *var = (Var *) node;
......@@ -353,6 +387,20 @@ ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)),
sublevels_up);
break;
case T_Iter:
ResolveNew(info, targetlist, (Node **) (&(((Iter *) node)->iterexpr)),
sublevels_up);
break;
case T_ArrayRef:
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refupperindexpr)),
sublevels_up);
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->reflowerindexpr)),
sublevels_up);
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refexpr)),
sublevels_up);
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refassgnexpr)),
sublevels_up);
break;
case T_Var:
{
int this_varno = (int) ((Var *) node)->varno;
......@@ -454,6 +502,38 @@ nodeHandleRIRAttributeRule(Node **nodePtr,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *) node;
nodeHandleRIRAttributeRule((Node **) (&(iter->iterexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *) node;
nodeHandleRIRAttributeRule((Node **) (&(ref->refupperindexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
nodeHandleRIRAttributeRule((Node **) (&(ref->reflowerindexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
nodeHandleRIRAttributeRule((Node **) (&(ref->refexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
nodeHandleRIRAttributeRule((Node **) (&(ref->refassgnexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
}
break;
case T_Var:
{
int this_varno = ((Var *) node)->varno;
......@@ -598,6 +678,33 @@ nodeHandleViewRule(Node **nodePtr,
rt_index, modified, sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *) node;
nodeHandleViewRule((Node **) (&(iter->iterexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *) node;
nodeHandleViewRule((Node **) (&(ref->refupperindexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
nodeHandleViewRule((Node **) (&(ref->reflowerindexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
nodeHandleViewRule((Node **) (&(ref->refexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
nodeHandleViewRule((Node **) (&(ref->refassgnexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_Var:
{
Var *var = (Var *) node;
......
......@@ -3,7 +3,7 @@
* out of it's tuple
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.3 1998/09/01 04:32:49 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.4 1998/10/02 16:27:51 momjian Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
......@@ -52,9 +52,22 @@
#include "utils/lsyscache.h"
#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "fmgr.h"
/* ----------
* Local data types
* ----------
*/
typedef struct QryHier {
struct QryHier *parent;
Query *query;
} QryHier;
/* ----------
* Global data
* ----------
......@@ -64,6 +77,10 @@ static void *plan_getrule = NULL;
static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
static void *plan_getview = NULL;
static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or rulename = $2";
static void *plan_getam = NULL;
static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
static void *plan_getopclass = NULL;
static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
/* ----------
......@@ -72,6 +89,8 @@ static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or ru
*/
text *pg_get_ruledef(NameData *rname);
text *pg_get_viewdef(NameData *rname);
text *pg_get_indexdef(Oid indexrelid);
NameData *pg_get_userbyid(int4 uid);
/* ----------
......@@ -80,15 +99,16 @@ text *pg_get_viewdef(NameData *rname);
*/
static char *make_ruledef(HeapTuple ruletup, TupleDesc rulettc);
static char *make_viewdef(HeapTuple ruletup, TupleDesc rulettc);
static char *get_query_def(Query *query);
static char *get_select_query_def(Query *query);
static char *get_insert_query_def(Query *query);
static char *get_update_query_def(Query *query);
static char *get_delete_query_def(Query *query);
static char *get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix);
static char *get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix);
static char *get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix);
static char *get_query_def(Query *query, QryHier *parentqh);
static char *get_select_query_def(Query *query, QryHier *qh);
static char *get_insert_query_def(Query *query, QryHier *qh);
static char *get_update_query_def(Query *query, QryHier *qh);
static char *get_delete_query_def(Query *query, QryHier *qh);
static char *get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
static char *get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix);
static char *get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix);
static char *get_const_expr(Const *constval);
static char *get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
static char *get_relation_name(Oid relid);
static char *get_attribute_name(Oid relid, int2 attnum);
static bool check_if_rte_used(int rt_index, Node *node, int sup);
......@@ -288,6 +308,272 @@ pg_get_viewdef(NameData *rname)
}
/* ----------
* get_viewdef - Mainly the same thing, but we
* only return the SELECT part of a view
* ----------
*/
text *
pg_get_indexdef(Oid indexrelid)
{
text *indexdef;
HeapTuple ht_idx;
HeapTuple ht_idxrel;
HeapTuple ht_indrel;
HeapTuple spi_tup;
TupleDesc spi_ttc;
int spi_fno;
Form_pg_index idxrec;
Form_pg_class idxrelrec;
Form_pg_class indrelrec;
Datum spi_args[1];
char spi_nulls[2];
int spirc;
int len;
int keyno;
char buf[8192];
char keybuf[8192];
char *sep;
/* ----------
* Connect to SPI manager
* ----------
*/
if (SPI_connect() != SPI_OK_CONNECT)
elog(ERROR, "get_indexdef: cannot connect to SPI manager");
/* ----------
* On the first call prepare the plans to lookup pg_am
* and pg_opclass.
* ----------
*/
if (plan_getam == NULL)
{
Oid argtypes[1];
void *plan;
argtypes[0] = OIDOID;
plan = SPI_prepare(query_getam, 1, argtypes);
if (plan == NULL)
elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
plan_getam = SPI_saveplan(plan);
argtypes[0] = OIDOID;
plan = SPI_prepare(query_getopclass, 1, argtypes);
if (plan == NULL)
elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
plan_getopclass = SPI_saveplan(plan);
}
/* ----------
* Fetch the pg_index tuple by the Oid of the index
* ----------
*/
ht_idx = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexrelid), 0, 0, 0);
if (!HeapTupleIsValid(ht_idx))
elog(ERROR, "syscache lookup for index %d failed", indexrelid);
idxrec = (Form_pg_index)GETSTRUCT(ht_idx);
/* ----------
* Fetch the pg_class tuple of the index relation
* ----------
*/
ht_idxrel = SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(idxrec->indexrelid), 0, 0, 0);
if (!HeapTupleIsValid(ht_idxrel))
elog(ERROR, "syscache lookup for relid %d failed", idxrec->indexrelid);
idxrelrec = (Form_pg_class)GETSTRUCT(ht_idxrel);
/* ----------
* Fetch the pg_class tuple of the indexed relation
* ----------
*/
ht_indrel = SearchSysCacheTuple(RELOID,
ObjectIdGetDatum(idxrec->indrelid), 0, 0, 0);
if (!HeapTupleIsValid(ht_indrel))
elog(ERROR, "syscache lookup for relid %d failed", idxrec->indrelid);
indrelrec = (Form_pg_class)GETSTRUCT(ht_indrel);
/* ----------
* Get the am name for the index relation
* ----------
*/
spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
spi_nulls[0] = ' ';
spi_nulls[1] = '\0';
spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
if (SPI_processed != 1)
elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
spi_tup = SPI_tuptable->vals[0];
spi_ttc = SPI_tuptable->tupdesc;
spi_fno = SPI_fnumber(spi_ttc, "amname");
/* ----------
* Start the index definition
* ----------
*/
sprintf(buf, "CREATE %sINDEX %s ON %s USING %s (",
idxrec->indisunique ? "UNIQUE " : "",
nameout(&(idxrelrec->relname)),
nameout(&(indrelrec->relname)),
SPI_getvalue(spi_tup, spi_ttc, spi_fno));
/* ----------
* Collect the indexed attributes
* ----------
*/
sep = "";
keybuf[0] = '\0';
for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
{
if (idxrec->indkey[keyno] == InvalidAttrNumber)
break;
strcat(keybuf, sep);
sep = ", ";
/* ----------
* Add the indexed field name
* ----------
*/
if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1)
strcat(keybuf, "oid");
else
strcat(keybuf, get_attribute_name(idxrec->indrelid,
idxrec->indkey[keyno]));
/* ----------
* If not a functional index, add the operator class name
* ----------
*/
if (idxrec->indproc == InvalidOid)
{
spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
spi_nulls[0] = ' ';
spi_nulls[1] = '\0';
spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[keyno]);
if (SPI_processed != 1)
elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[keyno]);
spi_tup = SPI_tuptable->vals[0];
spi_ttc = SPI_tuptable->tupdesc;
spi_fno = SPI_fnumber(spi_ttc, "opcname");
strcat(keybuf, " ");
strcat(keybuf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
}
}
/* ----------
* For functional index say 'func (attrs) opclass'
* ----------
*/
if (idxrec->indproc != InvalidOid)
{
HeapTuple proctup;
Form_pg_proc procStruct;
proctup = SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(idxrec->indproc), 0, 0, 0);
if (!HeapTupleIsValid(proctup))
elog(ERROR, "cache lookup for proc %d failed", idxrec->indproc);
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
strcat(buf, nameout(&(procStruct->proname)));
strcat(buf, " (");
strcat(buf, keybuf);
strcat(buf, ") ");
spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
spi_nulls[0] = ' ';
spi_nulls[1] = '\0';
spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
if (spirc != SPI_OK_SELECT)
elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[0]);
if (SPI_processed != 1)
elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[0]);
spi_tup = SPI_tuptable->vals[0];
spi_ttc = SPI_tuptable->tupdesc;
spi_fno = SPI_fnumber(spi_ttc, "opcname");
strcat(buf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
}
else
/* ----------
* For the others say 'attr opclass [, ...]'
* ----------
*/
{
strcat(buf, keybuf);
}
/* ----------
* Finish
* ----------
*/
strcat(buf, ")");
/* ----------
* Create the result in upper executor memory
* ----------
*/
len = strlen(buf) + VARHDRSZ;
indexdef = SPI_palloc(len);
VARSIZE(indexdef) = len;
memcpy(VARDATA(indexdef), buf, len - VARHDRSZ);
/* ----------
* Disconnect from SPI manager
* ----------
*/
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "get_viewdef: SPI_finish() failed");
return indexdef;
}
/* ----------
* get_userbyid - Get a user name by usesysid and
* fallback to 'unknown (UID=n)'
* ----------
*/
NameData *
pg_get_userbyid(int4 uid)
{
HeapTuple usertup;
Form_pg_shadow user_rec;
NameData *result;
/* ----------
* Allocate space for the result
* ----------
*/
result = (NameData *) palloc(NAMEDATALEN);
memset(result->data, 0, NAMEDATALEN);
/* ----------
* Get the pg_shadow entry and print the result
* ----------
*/
usertup = SearchSysCacheTuple(USESYSID,
ObjectIdGetDatum(uid), 0, 0, 0);
if (HeapTupleIsValid(usertup))
{
user_rec = (Form_pg_shadow)GETSTRUCT(usertup);
StrNCpy(result->data, (&(user_rec->usename))->data, NAMEDATALEN);
}
else
{
sprintf((char *)result, "unknown (UID=%d)", uid);
}
return result;
}
/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
......@@ -331,16 +617,13 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
fno = SPI_fnumber(rulettc, "ev_qual");
ev_qual = SPI_getvalue(ruletup, rulettc, fno);
if (isnull)
ev_qual = NULL;
fno = SPI_fnumber(rulettc, "ev_action");
ev_action = SPI_getvalue(ruletup, rulettc, fno);
if (isnull)
ev_action = NULL;
if (ev_action != NULL)
actions = (List *) stringToNode(ev_action);
/* ----------
* Build the rules definition text
* ----------
......@@ -391,12 +674,15 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
{
Node *qual;
Query *query;
QryHier qh;
qual = stringToNode(ev_qual);
query = (Query *) lfirst(actions);
qh.parent = NULL;
qh.query = query;
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(query->rtable, 0, qual, TRUE));
strcat(buf, get_rule_expr(&qh, 0, qual, TRUE));
}
strcat(buf, " DO ");
......@@ -415,7 +701,7 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
foreach(action, actions)
{
query = (Query *) lfirst(action);
strcat(buf, get_query_def(query));
strcat(buf, get_query_def(query, NULL));
strcat(buf, "; ");
}
strcat(buf, ");");
......@@ -431,7 +717,7 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
Query *query;
query = (Query *) lfirst(actions);
strcat(buf, get_query_def(query));
strcat(buf, get_query_def(query, NULL));
strcat(buf, ";");
}
}
......@@ -482,13 +768,9 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
fno = SPI_fnumber(rulettc, "ev_qual");
ev_qual = SPI_getvalue(ruletup, rulettc, fno);
if (isnull)
ev_qual = "";
fno = SPI_fnumber(rulettc, "ev_action");
ev_action = SPI_getvalue(ruletup, rulettc, fno);
if (isnull)
ev_action = NULL;
if (ev_action != NULL)
actions = (List *) stringToNode(ev_action);
......@@ -500,7 +782,7 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, ""))
return "Not a view";
strcpy(buf, get_select_query_def(query));
strcpy(buf, get_query_def(query, NULL));
strcat(buf, ";");
/* ----------
......@@ -518,24 +800,29 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
* ----------
*/
static char *
get_query_def(Query *query)
get_query_def(Query *query, QryHier *parentqh)
{
QryHier qh;
qh.parent = parentqh;
qh.query = query;
switch (query->commandType)
{
case CMD_SELECT:
return get_select_query_def(query);
return get_select_query_def(query, &qh);
break;
case CMD_UPDATE:
return get_update_query_def(query);
return get_update_query_def(query, &qh);
break;
case CMD_INSERT:
return get_insert_query_def(query);
return get_insert_query_def(query, &qh);
break;
case CMD_DELETE:
return get_delete_query_def(query);
return get_delete_query_def(query, &qh);
break;
case CMD_NOTHING:
......@@ -557,7 +844,7 @@ get_query_def(Query *query)
* ----------
*/
static char *
get_select_query_def(Query *query)
get_select_query_def(Query *query, QryHier *qh)
{
char buf[8192];
char *sep;
......@@ -635,7 +922,7 @@ get_select_query_def(Query *query)
strcat(buf, sep);
sep = ", ";
strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
/* Check if we must say AS ... */
if (nodeTag(tle->expr) != T_Var)
......@@ -681,7 +968,7 @@ get_select_query_def(Query *query)
strcat(buf, sep);
sep = ", ";
strcat(buf, rte->relname);
if (rt_numused > 1)
if (strcmp(rte->relname, rte->refname) != 0)
{
strcat(buf, " ");
strcat(buf, rte->refname);
......@@ -694,7 +981,7 @@ get_select_query_def(Query *query)
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(query->rtable, 0, query->qual, (rt_numused > 1)));
strcat(buf, get_rule_expr(qh, 0, query->qual, (rt_numused > 1)));
}
/* Add the GROUP BY CLAUSE */
......@@ -706,7 +993,7 @@ get_select_query_def(Query *query)
{
strcat(buf, sep);
sep = ", ";
strcat(buf, get_rule_expr(query->rtable, 0, lfirst(l), (rt_numused > 1)));
strcat(buf, get_rule_expr(qh, 0, lfirst(l), (rt_numused > 1)));
}
}
......@@ -723,7 +1010,7 @@ get_select_query_def(Query *query)
* ----------
*/
static char *
get_insert_query_def(Query *query)
get_insert_query_def(Query *query, QryHier *qh)
{
char buf[8192];
char *sep;
......@@ -810,12 +1097,12 @@ get_insert_query_def(Query *query)
strcat(buf, sep);
sep = ", ";
strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
}
strcat(buf, ")");
}
else
strcat(buf, get_select_query_def(query));
strcat(buf, get_query_def(query, qh));
/* ----------
* Copy the query string into allocated space and return it
......@@ -830,7 +1117,7 @@ get_insert_query_def(Query *query)
* ----------
*/
static char *
get_update_query_def(Query *query)
get_update_query_def(Query *query, QryHier *qh)
{
char buf[8192];
char *sep;
......@@ -857,7 +1144,7 @@ get_update_query_def(Query *query)
sep = ", ";
strcat(buf, tle->resdom->resname);
strcat(buf, " = ");
strcat(buf, get_tle_expr(query->rtable, query->resultRelation,
strcat(buf, get_tle_expr(qh, query->resultRelation,
tle, TRUE));
}
......@@ -865,7 +1152,7 @@ get_update_query_def(Query *query)
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(query->rtable, query->resultRelation,
strcat(buf, get_rule_expr(qh, query->resultRelation,
query->qual, TRUE));
}
......@@ -882,7 +1169,7 @@ get_update_query_def(Query *query)
* ----------
*/
static char *
get_delete_query_def(Query *query)
get_delete_query_def(Query *query, QryHier *qh)
{
char buf[8192];
RangeTblEntry *rte;
......@@ -899,7 +1186,7 @@ get_delete_query_def(Query *query)
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
strcat(buf, get_rule_expr(query->rtable, 0, query->qual, FALSE));
strcat(buf, get_rule_expr(qh, 0, query->qual, FALSE));
}
/* ----------
......@@ -915,7 +1202,7 @@ get_delete_query_def(Query *query)
* ----------
*/
static char *
get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
{
char buf[8192];
......@@ -936,7 +1223,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
{
TargetEntry *tle = (TargetEntry *) node;
return get_rule_expr(rtable, rt_index,
return get_rule_expr(qh, rt_index,
(Node *) (tle->expr), varprefix);
}
break;
......@@ -947,7 +1234,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
strcat(buf, agg->aggname);
strcat(buf, "(");
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) (agg->target), varprefix));
strcat(buf, ")");
return pstrdup(buf);
......@@ -958,7 +1245,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
{
GroupClause *grp = (GroupClause *) node;
return get_rule_expr(rtable, rt_index,
return get_rule_expr(qh, rt_index,
(Node *) (grp->entry), varprefix);
}
break;
......@@ -974,13 +1261,13 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
switch (expr->opType)
{
case OP_EXPR:
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, " ");
strcat(buf, get_opname(((Oper *) expr->oper)->opno));
strcat(buf, " ");
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_rightop(expr),
varprefix));
return pstrdup(buf);
......@@ -988,11 +1275,11 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case OR_EXPR:
strcat(buf, "(");
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, ") OR (");
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_rightop(expr),
varprefix));
strcat(buf, ")");
......@@ -1001,11 +1288,11 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case AND_EXPR:
strcat(buf, "(");
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, ") AND (");
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_rightop(expr),
varprefix));
strcat(buf, ")");
......@@ -1014,7 +1301,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case NOT_EXPR:
strcat(buf, "NOT (");
strcat(buf, get_rule_expr(rtable, rt_index,
strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, ")");
......@@ -1022,7 +1309,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
break;
case FUNC_EXPR:
return get_func_expr(rtable, rt_index,
return get_func_expr(qh, rt_index,
(Expr *) node,
varprefix);
break;
......@@ -1037,7 +1324,14 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case T_Var:
{
Var *var = (Var *) node;
RangeTblEntry *rte = (RangeTblEntry *) nth(var->varno - 1, rtable);
RangeTblEntry *rte;
int sup = var->varlevelsup;
while(sup-- > 0) qh = qh->parent;
rte = (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable);
if (qh->parent == NULL && var->varlevelsup > 0)
rte = (RangeTblEntry *) nth(var->varno + 1, qh->query->rtable);
if (!strcmp(rte->refname, "*NEW*"))
strcat(buf, "new.");
......@@ -1047,7 +1341,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
strcat(buf, "current.");
else
{
if (varprefix && var->varno != rt_index)
if (strcmp(rte->relname, rte->refname) != 0)
{
strcat(buf, rte->refname);
strcat(buf, ".");
......@@ -1069,30 +1363,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) (sublink->subselect);
List *l;
char *sep;
if (sublink->lefthand != NULL)
{
strcat(buf, "(");
sep = "";
foreach(l, sublink->lefthand)
{
strcat(buf, sep);
sep = ", ";
strcat(buf, get_rule_expr(rtable, rt_index,
lfirst(l), varprefix));
}
strcat(buf, ") IN ");
}
strcat(buf, "(");
strcat(buf, get_query_def(query));
strcat(buf, ")");
return pstrdup(buf);
return get_sublink_expr(qh, rt_index, node, varprefix);
}
break;
......@@ -1116,7 +1387,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
* ----------
*/
static char *
get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
{
char buf[8192];
HeapTuple proctup;
......@@ -1143,7 +1414,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
if (!strcmp(proname, "nullvalue"))
{
strcpy(buf, "(");
strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
varprefix));
strcat(buf, ") ISNULL");
return pstrdup(buf);
......@@ -1151,7 +1422,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
if (!strcmp(proname, "nonnullvalue"))
{
strcpy(buf, "(");
strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
varprefix));
strcat(buf, ") NOTNULL");
return pstrdup(buf);
......@@ -1169,7 +1440,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
{
strcat(buf, sep);
sep = ", ";
strcat(buf, get_rule_expr(rtable, rt_index, lfirst(l), varprefix));
strcat(buf, get_rule_expr(qh, rt_index, lfirst(l), varprefix));
}
strcat(buf, ")");
......@@ -1194,7 +1465,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
* ----------
*/
static char *
get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
{
HeapTuple proctup;
Form_pg_proc procStruct;
......@@ -1208,12 +1479,12 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
* ----------
*/
if (tle->resdom->restypmod < 0)
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if (nodeTag(tle->expr) != T_Expr)
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
expr = (Expr *) (tle->expr);
if (expr->opType != FUNC_EXPR)
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
func = (Func *) (expr->oper);
......@@ -1235,11 +1506,11 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
* ----------
*/
if (procStruct->pronargs != 2)
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if (procStruct->prorettype != procStruct->proargtypes[0])
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if (procStruct->proargtypes[1] != INT4OID)
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
/* ----------
* Finally (to be totally safe) the second argument must be a
......@@ -1248,15 +1519,15 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
*/
second_arg = (Const *) nth(1, expr->args);
if (nodeTag((Node *) second_arg) != T_Const)
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if ((int4) (second_arg->constvalue) != tle->resdom->restypmod)
return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
return get_rule_expr(qh, rt_index, tle->expr, varprefix);
/* ----------
* Whow - got it. Now get rid of the padding function
* ----------
*/
return get_rule_expr(rtable, rt_index, lfirst(expr->args), varprefix);
return get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix);
}
......@@ -1274,6 +1545,7 @@ get_const_expr(Const *constval)
char *extval;
bool isnull = FALSE;
char buf[8192];
char namebuf[64];
if (constval->constisnull)
return "NULL";
......@@ -1289,7 +1561,83 @@ get_const_expr(Const *constval)
extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
&isnull, -1);
sprintf(buf, "'%s'::%s", extval, nameout(&(typeStruct->typname)));
sprintf(namebuf, "::%s", nameout(&(typeStruct->typname)));
if (strcmp(namebuf, "::unknown") == 0)
namebuf[0] = '\0';
sprintf(buf, "'%s'%s", extval, namebuf);
return pstrdup(buf);
}
/* ----------
* get_sublink_expr - Parse back a sublink
* ----------
*/
static char *
get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) (sublink->subselect);
Expr *expr;
List *l;
char *sep;
char buf[8192];
buf[0] = '\0';
if (sublink->lefthand != NULL)
{
if (length(sublink->lefthand) > 1)
strcat(buf, "(");
sep = "";
foreach(l, sublink->lefthand)
{
strcat(buf, sep);
sep = ", ";
strcat(buf, get_rule_expr(qh, rt_index,
lfirst(l), varprefix));
}
if (length(sublink->lefthand) > 1)
strcat(buf, ") ");
else
strcat(buf, " ");
}
switch (sublink->subLinkType) {
case EXISTS_SUBLINK:
strcat(buf, "EXISTS ");
break;
case ANY_SUBLINK:
expr = (Expr *)lfirst(sublink->oper);
strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
strcat(buf, " ANY ");
break;
case ALL_SUBLINK:
expr = (Expr *)lfirst(sublink->oper);
strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
strcat(buf, " ALL ");
break;
case EXPR_SUBLINK:
expr = (Expr *)lfirst(sublink->oper);
strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
strcat(buf, " ");
break;
default:
elog(ERROR, "unupported sublink type %d",
sublink->subLinkType);
break;
}
strcat(buf, "(");
strcat(buf, get_query_def(query, qh));
strcat(buf, ")");
return pstrdup(buf);
}
......
......@@ -26,7 +26,7 @@
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.55 1998/09/09 18:16:36 momjian Exp $
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.56 1998/10/02 16:27:53 momjian Exp $
#
#-------------------------------------------------------------------------
......@@ -435,6 +435,7 @@ echo "REVOKE ALL on pg_shadow FROM public" | \
echo "Creating view pg_rules"
echo "CREATE TABLE xpg_rules ( \
tablename name, \
rulename name, \
definition text);" | postgres $PGSQL_OPT template1 > /dev/null
#move it into pg_rules
......@@ -445,12 +446,18 @@ echo "UPDATE pg_type SET typname = 'pg_rules' WHERE typname = 'xpg_rules';" |\
mv $PGDATA/base/template1/xpg_rules $PGDATA/base/template1/pg_rules
echo "CREATE RULE \"_RETpg_rules\" AS ON SELECT TO pg_rules DO INSTEAD \
SELECT rulename, pg_get_ruledef(rulename) AS definition \
FROM pg_rewrite;" | postgres $PGSQL_OPT template1 > /dev/null
SELECT C.relname AS tablename, \
R.rulename AS rulename, \
pg_get_ruledef(R.rulename) AS definition \
FROM pg_rewrite R, pg_class C \
WHERE R.rulename !~ '^_RET' \
AND C.oid = R.ev_class;" | \
postgres $PGSQL_OPT template1 > /dev/null
echo "Creating view pg_views"
echo "CREATE TABLE xpg_views ( \
viewname name, \
viewowner name, \
definition text);" | postgres $PGSQL_OPT template1 > /dev/null
#move it into pg_views
echo "UPDATE pg_class SET relname = 'pg_views' WHERE relname = 'xpg_views';" |\
......@@ -461,11 +468,57 @@ mv $PGDATA/base/template1/xpg_views $PGDATA/base/template1/pg_views
echo "CREATE RULE \"_RETpg_views\" AS ON SELECT TO pg_views DO INSTEAD \
SELECT relname AS viewname, \
pg_get_userbyid(relowner) AS viewowner, \
pg_get_viewdef(relname) AS definition \
FROM pg_class WHERE relhasrules AND \
pg_get_viewdef(relname) != 'Not a view';" | \
postgres $PGSQL_OPT template1 > /dev/null
echo "Creating view pg_tables"
echo "CREATE TABLE xpg_tables ( \
tablename name, \
tableowner name, \
hasindexes bool, \
hasrules bool, \
hastriggers bool);" | postgres $PGSQL_OPT template1 > /dev/null
#move it into pg_tables
echo "UPDATE pg_class SET relname = 'pg_tables' WHERE relname = 'xpg_tables';" |\
postgres $PGSQL_OPT template1 > /dev/null
echo "UPDATE pg_type SET typname = 'pg_tables' WHERE typname = 'xpg_tables';" |\
postgres $PGSQL_OPT template1 > /dev/null
mv $PGDATA/base/template1/xpg_tables $PGDATA/base/template1/pg_tables
echo "CREATE RULE \"_RETpg_tables\" AS ON SELECT TO pg_tables DO INSTEAD \
SELECT relname AS tablename, \
pg_get_userbyid(relowner) AS tableowner, \
relhasindex AS hasindexes, \
relhasrules AS hasrules, \
(reltriggers > 0) AS hastriggers \
FROM pg_class WHERE relkind IN ('r', 's') \
AND pg_get_viewdef(relname) = 'Not a view';" | \
postgres $PGSQL_OPT template1 > /dev/null
echo "Creating view pg_indexes"
echo "CREATE TABLE xpg_indexes ( \
tablename name, \
indexname name, \
indexdef text);" | postgres $PGSQL_OPT template1 > /dev/null
#move it into pg_indexes
echo "UPDATE pg_class SET relname = 'pg_indexes' WHERE relname = 'xpg_indexes';" |\
postgres $PGSQL_OPT template1 > /dev/null
echo "UPDATE pg_type SET typname = 'pg_indexes' WHERE typname = 'xpg_indexes';" |\
postgres $PGSQL_OPT template1 > /dev/null
mv $PGDATA/base/template1/xpg_indexes $PGDATA/base/template1/pg_indexes
echo "CREATE RULE \"_RETpg_indexes\" AS ON SELECT TO pg_indexes DO INSTEAD \
SELECT C.relname AS tablename, \
I.relname AS indexname, \
pg_get_indexdef(X.indexrelid) AS indexdef \
FROM pg_index X, pg_class C, pg_class I \
WHERE C.oid = X.indrelid \
AND I.oid = X.indexrelid;" | \
postgres $PGSQL_OPT template1 > /dev/null
echo "Loading pg_description"
echo "copy pg_description from '$TEMPLATE_DESCR'" | \
postgres $PGSQL_OPT template1 > /dev/null
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_proc.h,v 1.69 1998/09/01 04:35:10 momjian Exp $
* $Id: pg_proc.h,v 1.70 1998/10/02 16:27:55 momjian Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
......@@ -2040,6 +2040,10 @@ DATA(insert OID = 1640 ( pg_get_ruledef PGUID 11 f t f 1 f 25 "19" 100 0 0 1
DESCR("source text of a rule");
DATA(insert OID = 1641 ( pg_get_viewdef PGUID 11 f t f 1 f 25 "19" 100 0 0 100 foo bar ));
DESCR("select statement of a view");
DATA(insert OID = 1642 ( pg_get_userbyid PGUID 11 f t f 1 f 19 "23" 100 0 0 100 foo bar ));
DESCR("user name by UID (with fallback)");
DATA(insert OID = 1643 ( pg_get_indexdef PGUID 11 f t f 1 f 25 "26" 100 0 0 100 foo bar ));
DESCR("index description");
/*
* prototypes for functions pg_proc.c
......
......@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: locks.h,v 1.9 1998/09/01 04:37:57 momjian Exp $
* $Id: locks.h,v 1.10 1998/10/02 16:27:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -19,5 +19,6 @@
extern List *matchLocks(CmdType event, RuleLock *rulelocks, int varno,
Query *parsetree);
extern void checkLockPerms(List *locks, Query *parsetree, int rt_index);
#endif /* LOCKS_H */
......@@ -671,3 +671,181 @@ QUERY: select * from rtest_nothn3;
200|OK
(2 rows)
QUERY: create table rtest_view1 (a int4, b text, v bool);
QUERY: create table rtest_view2 (a int4);
QUERY: create table rtest_view3 (a int4, b text);
QUERY: create table rtest_view4 (a int4, b text, c int4);
QUERY: create view rtest_vview1 as select a, b from rtest_view1 X
where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
QUERY: create view rtest_vview2 as select a, b from rtest_view1 where v;
QUERY: create view rtest_vview3 as select a, b from rtest_vview2 X
where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
QUERY: create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount
from rtest_view1 X, rtest_view2 Y
where X.a = Y.a
group by X.a, X.b;
QUERY: create function rtest_viewfunc1(int4) returns int4 as
'select count(*) from rtest_view2 where a = $1'
language 'sql';
QUERY: create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount
from rtest_view1;
QUERY: insert into rtest_view1 values (1, 'item 1', 't');
QUERY: insert into rtest_view1 values (2, 'item 2', 't');
QUERY: insert into rtest_view1 values (3, 'item 3', 't');
QUERY: insert into rtest_view1 values (4, 'item 4', 'f');
QUERY: insert into rtest_view1 values (5, 'item 5', 't');
QUERY: insert into rtest_view1 values (6, 'item 6', 'f');
QUERY: insert into rtest_view1 values (7, 'item 7', 't');
QUERY: insert into rtest_view1 values (8, 'item 8', 't');
QUERY: insert into rtest_view2 values (2);
QUERY: insert into rtest_view2 values (2);
QUERY: insert into rtest_view2 values (4);
QUERY: insert into rtest_view2 values (5);
QUERY: insert into rtest_view2 values (7);
QUERY: insert into rtest_view2 values (7);
QUERY: insert into rtest_view2 values (7);
QUERY: insert into rtest_view2 values (7);
QUERY: select * from rtest_vview1;
a|b
-+------
2|item 2
4|item 4
5|item 5
7|item 7
(4 rows)
QUERY: select * from rtest_vview2;
a|b
-+------
1|item 1
2|item 2
3|item 3
5|item 5
7|item 7
8|item 8
(6 rows)
QUERY: select * from rtest_vview3;
a|b
-+------
2|item 2
5|item 5
7|item 7
(3 rows)
QUERY: select * from rtest_vview4;
a|b |refcount
-+------+--------
2|item 2| 2
4|item 4| 1
5|item 5| 1
7|item 7| 4
(4 rows)
QUERY: select * from rtest_vview5;
a|b |refcount
-+------+--------
1|item 1| 0
2|item 2| 2
3|item 3| 0
4|item 4| 1
5|item 5| 1
6|item 6| 0
7|item 7| 4
8|item 8| 0
(8 rows)
QUERY: insert into rtest_view3 select * from rtest_vview1 where a < 7;
QUERY: select * from rtest_view3;
a|b
-+------
2|item 2
4|item 4
5|item 5
(3 rows)
QUERY: delete from rtest_view3;
QUERY: insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2';
QUERY: select * from rtest_view3;
a|b
-+------
1|item 1
3|item 3
7|item 7
8|item 8
(4 rows)
QUERY: delete from rtest_view3;
QUERY: insert into rtest_view3 select * from rtest_vview3;
QUERY: select * from rtest_view3;
a|b
-+------
2|item 2
5|item 5
7|item 7
(3 rows)
QUERY: delete from rtest_view3;
QUERY: insert into rtest_view4 select * from rtest_vview4 where 3 > refcount;
QUERY: select * from rtest_view4;
a|b |c
-+------+-
2|item 2|2
4|item 4|1
5|item 5|1
(3 rows)
QUERY: delete from rtest_view4;
QUERY: insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0;
QUERY: select * from rtest_view4;
a|b |c
-+------+-
3|item 3|0
6|item 6|0
8|item 8|0
(3 rows)
QUERY: delete from rtest_view4;
QUERY: create table rtest_comp (
part text,
unit char(4),
size float
);
QUERY: create table rtest_unitfact (
unit char(4),
factor float
);
QUERY: create view rtest_vcomp as
select X.part, (X.size * Y.factor) as size_in_cm
from rtest_comp X, rtest_unitfact Y
where X.unit = Y.unit;
QUERY: insert into rtest_unitfact values ('m', 100.0);
QUERY: insert into rtest_unitfact values ('cm', 1.0);
QUERY: insert into rtest_unitfact values ('inch', 2.54);
QUERY: insert into rtest_comp values ('p1', 'm', 5.0);
QUERY: insert into rtest_comp values ('p2', 'm', 3.0);
QUERY: insert into rtest_comp values ('p3', 'cm', 5.0);
QUERY: insert into rtest_comp values ('p4', 'cm', 15.0);
QUERY: insert into rtest_comp values ('p5', 'inch', 7.0);
QUERY: insert into rtest_comp values ('p6', 'inch', 4.4);
QUERY: select * from rtest_vcomp order by part;
part|size_in_cm
----+----------
p1 | 500
p2 | 300
p3 | 5
p4 | 15
p5 | 17.78
p6 | 11.176
(6 rows)
QUERY: select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;
part|size_in_cm
----+----------
p1 | 500
p2 | 300
p5 | 17.78
p4 | 15
p6 | 11.176
(5 rows)
......@@ -404,3 +404,100 @@ insert into rtest_nothn2 select * from rtest_nothn4;
select * from rtest_nothn2;
select * from rtest_nothn3;
create table rtest_view1 (a int4, b text, v bool);
create table rtest_view2 (a int4);
create table rtest_view3 (a int4, b text);
create table rtest_view4 (a int4, b text, c int4);
create view rtest_vview1 as select a, b from rtest_view1 X
where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
create view rtest_vview2 as select a, b from rtest_view1 where v;
create view rtest_vview3 as select a, b from rtest_vview2 X
where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount
from rtest_view1 X, rtest_view2 Y
where X.a = Y.a
group by X.a, X.b;
create function rtest_viewfunc1(int4) returns int4 as
'select count(*) from rtest_view2 where a = $1'
language 'sql';
create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount
from rtest_view1;
insert into rtest_view1 values (1, 'item 1', 't');
insert into rtest_view1 values (2, 'item 2', 't');
insert into rtest_view1 values (3, 'item 3', 't');
insert into rtest_view1 values (4, 'item 4', 'f');
insert into rtest_view1 values (5, 'item 5', 't');
insert into rtest_view1 values (6, 'item 6', 'f');
insert into rtest_view1 values (7, 'item 7', 't');
insert into rtest_view1 values (8, 'item 8', 't');
insert into rtest_view2 values (2);
insert into rtest_view2 values (2);
insert into rtest_view2 values (4);
insert into rtest_view2 values (5);
insert into rtest_view2 values (7);
insert into rtest_view2 values (7);
insert into rtest_view2 values (7);
insert into rtest_view2 values (7);
select * from rtest_vview1;
select * from rtest_vview2;
select * from rtest_vview3;
select * from rtest_vview4;
select * from rtest_vview5;
insert into rtest_view3 select * from rtest_vview1 where a < 7;
select * from rtest_view3;
delete from rtest_view3;
insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2';
select * from rtest_view3;
delete from rtest_view3;
insert into rtest_view3 select * from rtest_vview3;
select * from rtest_view3;
delete from rtest_view3;
insert into rtest_view4 select * from rtest_vview4 where 3 > refcount;
select * from rtest_view4;
delete from rtest_view4;
insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0;
select * from rtest_view4;
delete from rtest_view4;
--
-- Test for computations in views
--
create table rtest_comp (
part text,
unit char(4),
size float
);
create table rtest_unitfact (
unit char(4),
factor float
);
create view rtest_vcomp as
select X.part, (X.size * Y.factor) as size_in_cm
from rtest_comp X, rtest_unitfact Y
where X.unit = Y.unit;
insert into rtest_unitfact values ('m', 100.0);
insert into rtest_unitfact values ('cm', 1.0);
insert into rtest_unitfact values ('inch', 2.54);
insert into rtest_comp values ('p1', 'm', 5.0);
insert into rtest_comp values ('p2', 'm', 3.0);
insert into rtest_comp values ('p3', 'cm', 5.0);
insert into rtest_comp values ('p4', 'cm', 15.0);
insert into rtest_comp values ('p5', 'inch', 7.0);
insert into rtest_comp values ('p6', 'inch', 4.4);
select * from rtest_vcomp order by part;
select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment