mirror of
https://github.com/postgres/postgres.git
synced 2025-06-02 00:01:40 -04:00
258 lines
5.4 KiB
C
258 lines
5.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* locks.c
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.21 1999/07/16 04:59:39 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/heapam.h"
|
|
#include "catalog/pg_shadow.h"
|
|
#include "rewrite/locks.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
/*
|
|
* ThisLockWasTriggered
|
|
*
|
|
* walk the tree, if there we find a varnode,
|
|
* we check the varattno against the attnum
|
|
* if we find at least one such match, we return true
|
|
* otherwise, we return false
|
|
*/
|
|
static bool
|
|
nodeThisLockWasTriggered(Node *node, int varno, AttrNumber attnum,
|
|
int sublevels_up)
|
|
{
|
|
if (node == NULL)
|
|
return FALSE;
|
|
switch (nodeTag(node))
|
|
{
|
|
case T_Var:
|
|
{
|
|
Var *var = (Var *) node;
|
|
|
|
if (varno == var->varno &&
|
|
(attnum == var->varattno || attnum == -1))
|
|
return TRUE;
|
|
}
|
|
break;
|
|
case T_Expr:
|
|
{
|
|
Expr *expr = (Expr *) node;
|
|
|
|
return nodeThisLockWasTriggered((Node *) expr->args, varno,
|
|
attnum, sublevels_up);
|
|
}
|
|
break;
|
|
case T_TargetEntry:
|
|
{
|
|
TargetEntry *tle = (TargetEntry *) node;
|
|
|
|
return nodeThisLockWasTriggered(tle->expr, varno, attnum,
|
|
sublevels_up);
|
|
}
|
|
break;
|
|
case T_Aggref:
|
|
{
|
|
Aggref *aggref = (Aggref *) node;
|
|
|
|
return nodeThisLockWasTriggered(aggref->target, varno, attnum,
|
|
sublevels_up);
|
|
}
|
|
break;
|
|
case T_List:
|
|
{
|
|
List *l;
|
|
|
|
foreach(l, (List *) node)
|
|
{
|
|
if (nodeThisLockWasTriggered(lfirst(l), varno, attnum,
|
|
sublevels_up))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case T_SubLink:
|
|
{
|
|
SubLink *sublink = (SubLink *) node;
|
|
Query *query = (Query *) sublink->subselect;
|
|
|
|
return nodeThisLockWasTriggered(query->qual, varno, attnum,
|
|
sublevels_up + 1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* thisLockWasTriggered -
|
|
* walk the tree, if there we find a varnode, we check the varattno
|
|
* against the attnum if we find at least one such match, we return true
|
|
* otherwise, we return false
|
|
*/
|
|
static bool
|
|
thisLockWasTriggered(int varno,
|
|
AttrNumber attnum,
|
|
Query *parsetree)
|
|
{
|
|
|
|
if (nodeThisLockWasTriggered(parsetree->qual, varno, attnum, 0))
|
|
return true;
|
|
|
|
if (nodeThisLockWasTriggered((Node *) parsetree->targetList, varno, attnum, 0))
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/*
|
|
* matchLocks -
|
|
* match the list of locks and returns the matching rules
|
|
*/
|
|
List *
|
|
matchLocks(CmdType event,
|
|
RuleLock *rulelocks,
|
|
int varno,
|
|
Query *parsetree)
|
|
{
|
|
List *real_locks = NIL;
|
|
int nlocks;
|
|
int i;
|
|
|
|
Assert(rulelocks != NULL); /* we get called iff there is some lock */
|
|
Assert(parsetree != NULL);
|
|
|
|
if (parsetree->commandType != CMD_SELECT)
|
|
{
|
|
if (parsetree->resultRelation != varno)
|
|
return NULL;
|
|
}
|
|
|
|
nlocks = rulelocks->numLocks;
|
|
|
|
for (i = 0; i < nlocks; i++)
|
|
{
|
|
RewriteRule *oneLock = rulelocks->rules[i];
|
|
|
|
if (oneLock->event == event)
|
|
{
|
|
if (parsetree->commandType != CMD_SELECT ||
|
|
thisLockWasTriggered(varno,
|
|
oneLock->attrno,
|
|
parsetree))
|
|
real_locks = lappend(real_locks, oneLock);
|
|
}
|
|
}
|
|
|
|
checkLockPerms(real_locks, parsetree, varno);
|
|
|
|
return real_locks;
|
|
}
|
|
|
|
|
|
void
|
|
checkLockPerms(List *locks, Query *parsetree, int rt_index)
|
|
{
|
|
Relation ev_rel;
|
|
HeapTuple usertup;
|
|
char *evowner;
|
|
RangeTblEntry *rte;
|
|
int32 reqperm;
|
|
int32 aclcheck_res;
|
|
int i;
|
|
List *l;
|
|
|
|
if (locks == NIL)
|
|
return;
|
|
|
|
/*
|
|
* Get the usename of the rules event relation owner
|
|
*/
|
|
rte = (RangeTblEntry *) nth(rt_index - 1, parsetree->rtable);
|
|
ev_rel = heap_openr(rte->relname);
|
|
usertup = SearchSysCacheTuple(USESYSID,
|
|
ObjectIdGetDatum(ev_rel->rd_rel->relowner),
|
|
0, 0, 0);
|
|
if (!HeapTupleIsValid(usertup))
|
|
{
|
|
elog(ERROR, "cache lookup for userid %d failed",
|
|
ev_rel->rd_rel->relowner);
|
|
}
|
|
heap_close(ev_rel);
|
|
evowner = nameout(&(((Form_pg_shadow) GETSTRUCT(usertup))->usename));
|
|
|
|
/*
|
|
* Check all the locks, that should get fired on this query
|
|
*/
|
|
foreach(l, locks)
|
|
{
|
|
RewriteRule *onelock = (RewriteRule *) lfirst(l);
|
|
List *action;
|
|
|
|
/*
|
|
* In each lock check every action
|
|
*/
|
|
foreach(action, onelock->actions)
|
|
{
|
|
Query *query = (Query *) lfirst(action);
|
|
|
|
/*
|
|
* In each action check every rangetable entry for read/write
|
|
* permission of the event relations owner depending on if
|
|
* it's the result relation (write) or not (read)
|
|
*/
|
|
for (i = 2; i < length(query->rtable); i++)
|
|
{
|
|
if (i + 1 == query->resultRelation)
|
|
switch (query->resultRelation)
|
|
{
|
|
case CMD_INSERT:
|
|
reqperm = ACL_AP;
|
|
break;
|
|
default:
|
|
reqperm = ACL_WR;
|
|
break;
|
|
}
|
|
else
|
|
reqperm = ACL_RD;
|
|
|
|
rte = (RangeTblEntry *) nth(i, query->rtable);
|
|
aclcheck_res = pg_aclcheck(rte->relname,
|
|
evowner, reqperm);
|
|
if (aclcheck_res != ACLCHECK_OK)
|
|
{
|
|
elog(ERROR, "%s: %s",
|
|
rte->relname,
|
|
aclcheck_error_strings[aclcheck_res]);
|
|
}
|
|
|
|
/*
|
|
* So this is allowed due to the permissions of the rules
|
|
* event relation owner. But let's see if the next one too
|
|
*/
|
|
rte->skipAcl = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Phew, that was close
|
|
*/
|
|
return;
|
|
}
|