diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index f695d71107..aa2d944650 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -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); diff --git a/src/backend/rewrite/locks.c b/src/backend/rewrite/locks.c index f57a436420..5c9b0887a1 100644 --- a/src/backend/rewrite/locks.c +++ b/src/backend/rewrite/locks.c @@ -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; diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 20eff2fbb0..64d46efa40 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -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); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 7a4637e91e..0bbeeb0c51 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -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,25 +143,2051 @@ gatherRewriteMeta(Query *parsetree, return info; } -static List * -OptimizeRIRRules(List *locks) + +/* + * rangeTableEntry_used - + * we need to process a RTE for RIR rules only if it is + * referenced somewhere in var nodes of the query. + */ +static bool +rangeTableEntry_used(Node *node, int rt_index, int sublevels_up) { - List *attr_level = NIL, - *i; - List *relation_level = NIL; + if (node == NULL) + return FALSE; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + return rangeTableEntry_used( + (Node *)(tle->expr), + rt_index, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + return rangeTableEntry_used( + (Node *)(agg->target), + rt_index, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + return rangeTableEntry_used( + (Node *)(grp->entry), + rt_index, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + return rangeTableEntry_used( + (Node *)(exp->args), + rt_index, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + return rangeTableEntry_used( + (Node *)(iter->iterexpr), + rt_index, + sublevels_up); + } + break; + + 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; + + case T_Const: + return FALSE; + + case T_List: + { + List *l; + + foreach (l, (List *)node) { + if (rangeTableEntry_used( + (Node *)lfirst(l), + rt_index, + sublevels_up)) + return TRUE; + } + return FALSE; + } + break; + + case T_SubLink: + { + 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; + } + break; + + case T_Query: + { + 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; - 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); + + return FALSE; } + +/* + * attribute_used - + * Check if a specific attribute number of a RTE is used + * somewhere in the query + */ +static bool +attribute_used(Node *node, int rt_index, int attno, int sublevels_up) +{ + if (node == NULL) + return FALSE; + + switch(nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + + return attribute_used( + (Node *)(tle->expr), + rt_index, + attno, + sublevels_up); + } + break; + + case T_Aggreg: + { + Aggreg *agg = (Aggreg *)node; + + return attribute_used( + (Node *)(agg->target), + rt_index, + attno, + sublevels_up); + } + break; + + case T_GroupClause: + { + GroupClause *grp = (GroupClause *)node; + + return attribute_used( + (Node *)(grp->entry), + rt_index, + attno, + sublevels_up); + } + break; + + case T_Expr: + { + Expr *exp = (Expr *)node; + + return attribute_used( + (Node *)(exp->args), + rt_index, + attno, + sublevels_up); + } + break; + + case T_Iter: + { + Iter *iter = (Iter *)node; + + return attribute_used( + (Node *)(iter->iterexpr), + rt_index, + attno, + sublevels_up); + } + break; + + 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; + + 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; + + 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; + + + } + + return FALSE; +} + + +/* + * 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; + + 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(""); + 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. @@ -167,266 +2218,7 @@ orderRules(List *locks) return nconc(regular, instead_rules); } -static int -AllRetrieve(List *actions) -{ - List *n; - foreach(n, actions) - { - Query *pt = lfirst(n); - - /* - * 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; -} - -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; - - if ((rt_entry_locks = relation->rd_rules) == NULL) - return NIL; - - locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree); - - /* find all retrieve instead */ - foreach(i, locks) - { - RewriteRule *rule_lock = (RewriteRule *) lfirst(i); - - 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; - - relation_level = (rule_lock->attrno == -1); - if (rule_lock->actions == NIL) - { - *instead_flag = TRUE; - return NIL; - } - if (!rule_flag && - length(rule_lock->actions) >= 2 && - AllRetrieve(rule_lock->actions)) - { - *instead_flag = TRUE; - return rule_lock->actions; - } - ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation, - &modified); - if (modified) - { - *instead_flag = TRUE; - FixResdomTypes(parsetree->targetList); - return lcons(parsetree, NIL); - } - } - } - return NIL; -} - - -/* 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 - */ -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 */ - - /* - * 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; - - 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; - OffsetVarNodes(rule_action->qual, rt_length); - OffsetVarNodes((Node *) rule_action->targetList, rt_length); - OffsetVarNodes(rule_qual, rt_length); - - OffsetVarNodes((Node *) rule_action->groupClause, rt_length); - OffsetVarNodes((Node *) rule_action->havingQual, rt_length); - - 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); - - 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); - - 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); - - /* - * 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 List * -ProcessRetrieveQuery(Query *parsetree, - List *rtable, - bool *instead_flag, - bool rule) -{ - List *rt; - List *product_queries = NIL; - int rt_index = 0; - - - foreach(rt, rtable) - { - RangeTblEntry *rt_entry = lfirst(rt); - Relation rt_entry_relation = NULL; - List *result = NIL; - - rt_index++; - rt_entry_relation = heap_openr(rt_entry->relname); - - - - 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; - - 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); - - - 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); - } - } - return product_queries; -} static Query * CopyAndAddQual(Query *parsetree, @@ -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 - 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); + + if (rt_entry_locks != NULL) { + List *locks = + matchLocks(event, rt_entry_locks, result_relation, parsetree); - /* - * 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; - } - - return product_queries; + product_queries = + fireRules(parsetree, + result_relation, + event, + instead_flag, + locks, + qual_products); } - else - { - /* - * the statement is a select - */ - Query *other; + return product_queries; - /* - * ApplyRetrieveRule changes the range table XXX Unions are copied - * again. - */ - other = copyObject(parsetree); - - 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; +} + + diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 15c68064a6..87786d9cdd 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -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; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index f9774bf62f..b3a1db8c06 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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); } diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index 0117885d25..1ca47f40e2 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -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 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 06cb99d0c1..1f8ca51583 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -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 diff --git a/src/include/rewrite/locks.h b/src/include/rewrite/locks.h index b51b02463e..ad258d9063 100644 --- a/src/include/rewrite/locks.h +++ b/src/include/rewrite/locks.h @@ -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 */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 447b1499fa..f77a532d6e 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -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) + diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql index 6ca1877586..8ffefd5be0 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -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 >;