ING_DB
This commit is contained in:
parent
dcf464e14d
commit
2a2e856f72
|
|
@ -0,0 +1,869 @@
|
||||||
|
# include "gen.h"
|
||||||
|
# include "../h/dbs.h"
|
||||||
|
# include "../h/aux.h"
|
||||||
|
# include "../h/bs.h"
|
||||||
|
# include "../h/catalog.h"
|
||||||
|
# include "../h/symbol.h"
|
||||||
|
# include "../h/tree.h"
|
||||||
|
# include "qrymod.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
** PROTECT -- protection algorithm
|
||||||
|
**
|
||||||
|
** This module performs the DBS protection algorithm
|
||||||
|
**
|
||||||
|
** The basic algorithm is as follows:
|
||||||
|
**
|
||||||
|
** The algorithm is applied once to each variable used in the
|
||||||
|
** query. Each variable has an initial check performed to
|
||||||
|
** determine applicability -- if the current user owns the
|
||||||
|
** relation, or if the relation is specially marked as being
|
||||||
|
** "all access to everyone", then the algorithm is skipped,
|
||||||
|
** thereby having effectively no restriction.
|
||||||
|
**
|
||||||
|
** The set of all such variables is computed in 'protect',
|
||||||
|
** and then 'dopro' is called to process each of those. This
|
||||||
|
** is so the protection algorithm does not get applied recursively
|
||||||
|
** to constraints which define more than one variable. Notice
|
||||||
|
** that this could in itself be a protection violation, if it
|
||||||
|
** were acceptable to reference a relation you do not own in a
|
||||||
|
** PERMIT statement.
|
||||||
|
**
|
||||||
|
** The effective query mode for this variable is then computed.
|
||||||
|
** This is the same as the query mode of the whole query if
|
||||||
|
** the variable in question is the result variable, otherwise
|
||||||
|
** it is "retrieve" mode.
|
||||||
|
**
|
||||||
|
** The next step is to scan the query tree and create sets of
|
||||||
|
** domains referenced. Four sets are created:
|
||||||
|
** uset -- the set of domains updated (actually,
|
||||||
|
** referenced in the target list -- on a
|
||||||
|
** retrieve, this will be the set of domains
|
||||||
|
** retrieved to the user).
|
||||||
|
** rset -- the set of domains retrieved in some
|
||||||
|
** context other than the left hand side of
|
||||||
|
** an equal sign.
|
||||||
|
** aset -- the set of domains aggregated. This only
|
||||||
|
** includes domains aggregated with a simple
|
||||||
|
** aggregate (not an aggregate function) with
|
||||||
|
** no qualification, since it may be possible
|
||||||
|
** to come up with too much information other-
|
||||||
|
** wise.
|
||||||
|
** qset -- the set of domains retrieved for use in
|
||||||
|
** a qualification, but never stored. This
|
||||||
|
** includes domains in a qualification of an
|
||||||
|
** aggregate or aggregate function.
|
||||||
|
** For more details of domains in each of these sets, look at
|
||||||
|
** the routine 'makedset'.
|
||||||
|
**
|
||||||
|
** If we had a retrieve operation in the first place, we will
|
||||||
|
** then merge 'uset' into 'rset' and clear 'uset', so that
|
||||||
|
** now 'uset' only contains domains which are actually updated,
|
||||||
|
** and 'rset' contains all domains which are retrieved.
|
||||||
|
**
|
||||||
|
** Now that we know what is referenced, we can scan the AA_PROTECT
|
||||||
|
** catalog. We scan the entire catalog once for each variable
|
||||||
|
** mentioned in the query (except as already taken care of as
|
||||||
|
** described above).
|
||||||
|
**
|
||||||
|
** We must create a set of all operations on this variable which
|
||||||
|
** are not yet resolved, that is, for which no PERMIT statements
|
||||||
|
** which qualify have been issued. We store this set in the
|
||||||
|
** variable "noperm". As PERMIT statements are found, bits will
|
||||||
|
** be cleared. If the variable is not zero by the end of the
|
||||||
|
** scan of the AA_PROTECT catalog, then we reject the query,
|
||||||
|
** saying that we don't have permission -- giving us default
|
||||||
|
** to deny.
|
||||||
|
**
|
||||||
|
** For each tuple in the AA_PROTECT catalog for this relation,
|
||||||
|
** we call "proappl" to see if it applies to this query. This
|
||||||
|
** routine checks the user, terminal, time of day, and so forth
|
||||||
|
** (in fact, everything which is query-independent) and tells
|
||||||
|
** whether this tuple might apply.
|
||||||
|
**
|
||||||
|
** If the tuple passes this initial check, we then do the query-
|
||||||
|
** dependent checking. This amounts to calling "prochk" once
|
||||||
|
** for each operation (and domain set) in the query. What we
|
||||||
|
** get back is a set of operations which this tuple applies to.
|
||||||
|
** If zero, the tuple doesn't apply at all; otherwise, it
|
||||||
|
** applies to at least one operation. If it applies to some-
|
||||||
|
** thing, we call it "interesting".
|
||||||
|
**
|
||||||
|
** For "interesting" tuples, we now get the corresponding
|
||||||
|
** qualification (if one exists), and disjoin it to a set of
|
||||||
|
** protection constraints held in "pqual". Also, we mark
|
||||||
|
** any operations we found as having been done, by clearing
|
||||||
|
** bits in "noperm".
|
||||||
|
**
|
||||||
|
** When we have completed scanning the AA_PROTECT catalog,
|
||||||
|
** we check "noperm". If it is non-zero, then we have some
|
||||||
|
** operation for which a PERMIT statement has not been issued,
|
||||||
|
** and we issue an error message. If this variable is ok,
|
||||||
|
** then we go on and try the next variable.
|
||||||
|
**
|
||||||
|
** When all variables have been accounted for, we check to
|
||||||
|
** see if we have any qualifications collected from the
|
||||||
|
** protection algorithm. If so, we conjoin them to the
|
||||||
|
** query tree.
|
||||||
|
**
|
||||||
|
** Finally, we return the root of the modified tree. This
|
||||||
|
** tree is guaranteed to have no authorization violations,
|
||||||
|
** and may be run as a regular query.
|
||||||
|
**
|
||||||
|
** Parameters:
|
||||||
|
** root -- the root of the tree.
|
||||||
|
**
|
||||||
|
** Returns:
|
||||||
|
** The root of the modified and authorized tree.
|
||||||
|
**
|
||||||
|
** Side Effects:
|
||||||
|
** A possible non-local return on access violation.
|
||||||
|
**
|
||||||
|
** Defines:
|
||||||
|
** Proopmap -- a mapping from query modes to operation
|
||||||
|
** set bits in the AA_PROTECT catalog.
|
||||||
|
** protect -- the driver for the protection algorithm.
|
||||||
|
** dopro -- the guts of the algorithm.
|
||||||
|
** makedset -- the routine to make the domain sets.
|
||||||
|
** proappl -- to check if a protection tuple applies.
|
||||||
|
** prochk -- to check what operations it handles.
|
||||||
|
** pr_set -- debugging set print.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int Proopmap[MAXPROQM + 1] =
|
||||||
|
{
|
||||||
|
PRO_RETR, /* 0 -- mdRETTERM */
|
||||||
|
PRO_RETR, /* 1 -- mdRETR */
|
||||||
|
PRO_APP, /* 2 -- mdAPP */
|
||||||
|
PRO_REPL, /* 3 -- mdREPL */
|
||||||
|
PRO_DEL, /* 4 -- mdDEL */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern DESC Prodes;
|
||||||
|
extern char Terminal[PROTERM + 1];
|
||||||
|
extern QTREE *gettree();
|
||||||
|
|
||||||
|
QTREE *protect(r)
|
||||||
|
register QTREE *r;
|
||||||
|
{
|
||||||
|
register int vn;
|
||||||
|
register int qmode;
|
||||||
|
register int varset;
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
AAtTfp(50, -1, "PROTECT>\n");
|
||||||
|
# endif
|
||||||
|
|
||||||
|
varset = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Scan the range table and create a set of all variables
|
||||||
|
** which are 'interesting', that is, on which the protection
|
||||||
|
** algorithm should be performed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (vn = 0; vn < MAXVAR + 1; vn++)
|
||||||
|
{
|
||||||
|
if (!Rangev[vn].rused)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* if owner, accept any query */
|
||||||
|
if (AAbequal(Rangev[vn].rowner, AAusercode, 2))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* check for "no restriction" bit asserted (= clear) */
|
||||||
|
if ((Rangev[vn].rstat & S_PROTALL) == 0)
|
||||||
|
continue;
|
||||||
|
if ((Rangev[vn].rstat & S_PROTRET) == 0 &&
|
||||||
|
(Qmode == (int) mdRETR || Qmode == (int) mdRET_UNI))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
varset |= 1 << vn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** For each variable specified in varset (that is, for each
|
||||||
|
** variable in the initial query), do the real algorithm.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (vn = 0; vn < MAXVAR + 1; vn++)
|
||||||
|
{
|
||||||
|
if ((varset & (1 << vn)) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
AAtTfp(50, 1, "vn=%d: %.12s\n", vn, Rangev[vn].relid);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Determine the query mode for this variable. This
|
||||||
|
** is not the query mode of the original query,
|
||||||
|
** unless the variable is the result variable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
qmode = Qmode;
|
||||||
|
if (vn != Resultvar || qmode == (int) mdRET_UNI)
|
||||||
|
qmode = (int) mdRETTERM;
|
||||||
|
|
||||||
|
# ifdef xQTR3
|
||||||
|
if (qmode == (int) mdRETR || qmode > (int) mdDEL || qmode < 0)
|
||||||
|
AAsyserr(19041, qmode);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* do the interesting part of the algorithm */
|
||||||
|
dopro(vn, r, qmode, (short *) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return the (authorized) tree */
|
||||||
|
# ifdef xQTR1
|
||||||
|
if (AAtTf(50, 15))
|
||||||
|
treepr(r, "PROTECT->");
|
||||||
|
# endif
|
||||||
|
return (r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** DOPRO -- actually do the protection algorithm
|
||||||
|
**
|
||||||
|
** This is the guts of it, broken off because it must be called
|
||||||
|
** recursively on aggregates. The algorithm is as discussed
|
||||||
|
** in the module header.
|
||||||
|
**
|
||||||
|
** Parameters:
|
||||||
|
** varno -- the variable number of interest.
|
||||||
|
** root -- the root of the tree to modify.
|
||||||
|
** qmode -- the effective query mode for this relation.
|
||||||
|
** byset -- if non-NULL, a set of domains passed back
|
||||||
|
** which gets bound out of the aggregate func,
|
||||||
|
** in other words, the by list.
|
||||||
|
**
|
||||||
|
** Side Effects:
|
||||||
|
** The tree pointed at by 'root' gets modified.
|
||||||
|
** Quite possibly 'Rangev' and 'Remap' get clobbered.
|
||||||
|
*/
|
||||||
|
dopro(varno, root, qmode, byset)
|
||||||
|
int varno;
|
||||||
|
QTREE *root;
|
||||||
|
int qmode;
|
||||||
|
short byset[8];
|
||||||
|
{
|
||||||
|
short qset[8];
|
||||||
|
short uset[8];
|
||||||
|
short aset[8];
|
||||||
|
short rset[8];
|
||||||
|
short zeros[8];
|
||||||
|
QTREE *p;
|
||||||
|
QTREE *pqual;
|
||||||
|
register int i;
|
||||||
|
register int vn;
|
||||||
|
register QTREE *t;
|
||||||
|
int noperm;
|
||||||
|
int noqual;
|
||||||
|
struct protect prokey;
|
||||||
|
struct protect protup;
|
||||||
|
struct tup_id lotid;
|
||||||
|
struct tup_id hitid;
|
||||||
|
struct qvect
|
||||||
|
{
|
||||||
|
QTREE *q_qual;
|
||||||
|
int q_mode;
|
||||||
|
};
|
||||||
|
struct qvect quals[4];
|
||||||
|
int j;
|
||||||
|
extern QTREE *trimqlend();
|
||||||
|
extern QTREE *tree();
|
||||||
|
extern QTREE *norml();
|
||||||
|
|
||||||
|
|
||||||
|
t = root;
|
||||||
|
vn = varno;
|
||||||
|
|
||||||
|
/* create domain usage sets */
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
zeros[i] = uset[i] = rset[i] = qset[i] = aset[i] = 0;
|
||||||
|
if (byset != (short *) 0)
|
||||||
|
byset[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Create domain usage set for target list side. There are
|
||||||
|
** two general cases: this is the root of the tree, or this
|
||||||
|
** is the head of an aggregate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch (t->sym.type)
|
||||||
|
{
|
||||||
|
case AGHEAD:
|
||||||
|
/*
|
||||||
|
** An aggregate head falls into two classes: simple
|
||||||
|
** aggregate and aggregate function. In an aggregate
|
||||||
|
** function, care must be taken to bind the variables
|
||||||
|
** in the by-list outside of the aggregate. We use
|
||||||
|
** 'rset' as a temporary here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (t->left->sym.type == BYHEAD)
|
||||||
|
{
|
||||||
|
/* make by-list set */
|
||||||
|
makedset(vn, t->left->left, (short *) 0, rset, aset, qset);
|
||||||
|
|
||||||
|
/* merge by-list set into qualification set */
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (byset != (short *) 0)
|
||||||
|
byset[i] |= rset[i];
|
||||||
|
qset[i] |= rset[i];
|
||||||
|
rset[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make aggregate list set */
|
||||||
|
makedset(vn, t->left->right->right, (short *) 0, rset, aset, qset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* simple aggregate */
|
||||||
|
# ifdef xQTR3
|
||||||
|
if (t->left->sym.type != AOP)
|
||||||
|
AAsyserr(19042, t->left->sym.type);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* check for qualification */
|
||||||
|
if (t->right->sym.type == QLEND)
|
||||||
|
{
|
||||||
|
/* simple, unqualified aggregate */
|
||||||
|
makedset(vn, t->left->right, (short *) 0, aset, aset, qset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
# ifdef xQTR3
|
||||||
|
if (t->right->sym.type != AND)
|
||||||
|
AAsyserr(19043, t->right->sym.type);
|
||||||
|
# endif
|
||||||
|
makedset(vn, t->left->right, (short *) 0, rset, aset, qset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ROOT:
|
||||||
|
makedset(vn, t->left, uset, rset, aset, qset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* scan qualifcation */
|
||||||
|
makedset(vn, t->right, (short *) 0, qset, aset, qset);
|
||||||
|
|
||||||
|
/* if retrieval, drop the 'update' set */
|
||||||
|
/* if delete or append, force an appernt update */
|
||||||
|
switch (qmode)
|
||||||
|
{
|
||||||
|
# ifdef ENUM_INT
|
||||||
|
case mdRETTERM:
|
||||||
|
# else
|
||||||
|
case (int) mdRETTERM:
|
||||||
|
# endif
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
uset[i] = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
# ifdef ENUM_INT
|
||||||
|
case mdDEL:
|
||||||
|
case mdAPP:
|
||||||
|
# else
|
||||||
|
case (int) mdDEL:
|
||||||
|
case (int) mdAPP:
|
||||||
|
# endif
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
uset[i] = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
if (AAtTfp(51, 2, "qmode %d\n", qmode))
|
||||||
|
{
|
||||||
|
pr_set(uset, "uset");
|
||||||
|
pr_set(rset, "rset");
|
||||||
|
pr_set(aset, "aset");
|
||||||
|
pr_set(qset, "qset");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* create a bit map of all referenced operations */
|
||||||
|
noperm = 0;
|
||||||
|
if (!AAbequal(uset, zeros, sizeof zeros))
|
||||||
|
noperm |= Proopmap[qmode];
|
||||||
|
if (!AAbequal(rset, zeros, sizeof zeros))
|
||||||
|
noperm |= PRO_RETR;
|
||||||
|
if (!AAbequal(aset, zeros, sizeof zeros))
|
||||||
|
noperm |= PRO_AGGR;
|
||||||
|
if (!AAbequal(qset, zeros, sizeof zeros))
|
||||||
|
noperm |= PRO_TEST;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** if no oper, then the query was probably just aggr's
|
||||||
|
** if (noperm == 0)
|
||||||
|
** return;
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* initialize qualification portion */
|
||||||
|
for (i = 0; i < 4; )
|
||||||
|
quals[i++].q_qual = (QTREE *) 0;
|
||||||
|
noqual = FALSE;
|
||||||
|
|
||||||
|
/* check the AA_PROTECT catalog */
|
||||||
|
opencatalog(AA_PROTECT, 0);
|
||||||
|
AAam_setkey(&Prodes, &prokey, Rangev[vn].relid, PRORELID);
|
||||||
|
AAam_setkey(&Prodes, &prokey, Rangev[vn].rowner, PRORELOWN);
|
||||||
|
AAam_find(&Prodes, EXACTKEY, &lotid, &hitid, &prokey);
|
||||||
|
|
||||||
|
while ((i = AAam_get(&Prodes, &lotid, &hitid, &protup, TRUE)) == 0)
|
||||||
|
{
|
||||||
|
if (AAkcompare(&Prodes, &prokey, &protup) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
# ifdef xQTR2
|
||||||
|
if (AAtTfp(51, 4, "<PROTECT "))
|
||||||
|
AAprtup(&Prodes, &protup);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* check if this is the correct user, terminal, etc */
|
||||||
|
if (!proappl(&protup))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* alright, let's check the operation */
|
||||||
|
i = 0;
|
||||||
|
if (qmode != (int) mdRETTERM)
|
||||||
|
i = quals[0].q_mode = prochk(Proopmap[qmode], uset, &protup);
|
||||||
|
i |= quals[1].q_mode = prochk(PRO_RETR, rset, &protup);
|
||||||
|
i |= quals[2].q_mode = prochk(PRO_AGGR, aset, &protup);
|
||||||
|
i |= quals[3].q_mode = prochk(PRO_TEST, qset, &protup);
|
||||||
|
|
||||||
|
# ifdef xQTR2
|
||||||
|
AAtTfp(51, 5, "Satisfies operations 0%o\n", i);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* see if this tuple is "interesting" */
|
||||||
|
if (i == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* it is! get the qualification (if any) */
|
||||||
|
if (protup.protree >= 0)
|
||||||
|
{
|
||||||
|
p = gettree(Rangev[vn].relid, Rangev[vn].rowner, mdPROT, protup.protree, FALSE);
|
||||||
|
# ifdef xQTR2
|
||||||
|
if (AAtTf(51, 6))
|
||||||
|
treepr(p, "Protection Clause");
|
||||||
|
# endif
|
||||||
|
p = trimqlend(p->right);
|
||||||
|
# ifdef xQTR3
|
||||||
|
/* check for a non-NULL qualification */
|
||||||
|
if (p == (QTREE *) 0)
|
||||||
|
AAsyserr(19044);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* translate to the interesting variable */
|
||||||
|
j = protup.proresvar;
|
||||||
|
if (Remap[j] >= 0)
|
||||||
|
j = Remap[j];
|
||||||
|
mergevar(j, varno, p);
|
||||||
|
|
||||||
|
/* disjoin the protection qual to real qual */
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
if (quals[j].q_mode == 0)
|
||||||
|
continue;
|
||||||
|
if (quals[j].q_qual == (QTREE *) 0)
|
||||||
|
quals[j].q_qual = p;
|
||||||
|
else
|
||||||
|
quals[j].q_qual = tree(quals[j].q_qual, p, OR, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
noqual = TRUE;
|
||||||
|
|
||||||
|
/* mark this operation as having been handled */
|
||||||
|
noperm &= ~i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test 'get' return code */
|
||||||
|
if (i < 0)
|
||||||
|
AAsyserr(19045);
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
AAtTfp(51, 12, "No perm on 0%o\n", noperm);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* see if no tuples applied for some operation */
|
||||||
|
if (noperm != 0)
|
||||||
|
far_error(3500, Qmode, vn, (char *) 0);
|
||||||
|
|
||||||
|
/* see if we want to modify the query at all */
|
||||||
|
if (!noqual)
|
||||||
|
{
|
||||||
|
/* conjoin the qualification */
|
||||||
|
pqual = (QTREE *) 0;
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
if (quals[i].q_qual != (QTREE *) 0)
|
||||||
|
if (pqual == (QTREE *) 0)
|
||||||
|
pqual = quals[i].q_qual;
|
||||||
|
else
|
||||||
|
pqual = tree(pqual, quals[i].q_qual, AND, 0);
|
||||||
|
pqual = tree(pqual, tree((QTREE *) 0, (QTREE *) 0, QLEND, 0), AND, 0);
|
||||||
|
appqual(pqual, t);
|
||||||
|
|
||||||
|
/* normalize the tree */
|
||||||
|
t->right = norml(trimqlend(t->right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** MAKEDSET -- make domain reference sets
|
||||||
|
**
|
||||||
|
** This routine creates some sets which reflect the usage of
|
||||||
|
** domains for a particular variable.
|
||||||
|
**
|
||||||
|
** The interesting nodes are 'case' labels in the large
|
||||||
|
** switch statement which comprises most of the code. To
|
||||||
|
** describe briefly:
|
||||||
|
**
|
||||||
|
** VAR nodes are easy: if they are for the current variable,
|
||||||
|
** set the bit corresponding to the domain in the
|
||||||
|
** 'retrieval' set. They can have no descendents,
|
||||||
|
** so just return.
|
||||||
|
** RESDOM nodes are also easy: they can be handled the same,
|
||||||
|
** but the bit is set in the 'update' set instead.
|
||||||
|
** AGHEAD nodes signal the beginning of an aggregate or
|
||||||
|
** aggregate function. In this case, we scan the
|
||||||
|
** qualification first (noting that RESDOM and VAR
|
||||||
|
** nodes are processed as 'qualification' sets
|
||||||
|
** instead of 'retrieval' or 'update' sets). Then,
|
||||||
|
** if the aggregate has a WHERE clause or a BY list,
|
||||||
|
** we treat it as a retrieve; otherwise, we call our-
|
||||||
|
** selves recursively treating VAR nodes as 'aggregate'
|
||||||
|
** types rather than 'retrieve' types.
|
||||||
|
** BYHEAD nodes signal the beginning of a BY list. The left
|
||||||
|
** subtree (the actual BY-list) is processed with
|
||||||
|
** RESDOM nodes ignored (since they are pseudo-domains
|
||||||
|
** anyhow) and VAR nodes mapped into the 'qualification'
|
||||||
|
** set. Then we check the right subtree (which better
|
||||||
|
** begin with an AOP node!) and continue processing.
|
||||||
|
** AOP nodes must have a NULL left subtree, so we just drop
|
||||||
|
** to the right subtree and iterate. Notice that we
|
||||||
|
** do NOT map VAR nodes into the 'aggregate' set for
|
||||||
|
** this node, since this has already been done by the
|
||||||
|
** AGHEAD node; also, this aggregate might be counted
|
||||||
|
** as a retrieve operation instead of an aggregate
|
||||||
|
** operation (as far as the protection system is con-
|
||||||
|
** cerned) -- this has been handled by the AGHEAD
|
||||||
|
** node.
|
||||||
|
** All other nodes are processed recursively along both edges.
|
||||||
|
**
|
||||||
|
** Parameters:
|
||||||
|
** vn -- the variable number that we are currently
|
||||||
|
** interested in.
|
||||||
|
** tree -- the root of the tree to scan. Notice that this
|
||||||
|
** will in general be only one half of the tree --
|
||||||
|
** makedset will be called once for the target
|
||||||
|
** list and once for the qualification, with
|
||||||
|
** different sets for the following parameters.
|
||||||
|
** uset -- adjusted to be the set of all domains
|
||||||
|
** updated.
|
||||||
|
** rset -- adjusted to be the set of all domains
|
||||||
|
** retrieved implicitly, that is, on the right-
|
||||||
|
** hand-side of an assignment operator.
|
||||||
|
** aset -- adjusted to be the set of all domains
|
||||||
|
** aggregated. Notice that this set is not
|
||||||
|
** adjusted explicitly, but rather is passed
|
||||||
|
** to recursive incarnations of this routine
|
||||||
|
** as 'rset'.
|
||||||
|
** qset -- adjusted to be the set of domains retrieved
|
||||||
|
** implicitly in a qualification. Like 'aset',
|
||||||
|
** this is passed as 'rset' to recursive
|
||||||
|
** incarnations.
|
||||||
|
*/
|
||||||
|
makedset(vn, t, uset, rset, aset, qset)
|
||||||
|
int vn;
|
||||||
|
register QTREE *t;
|
||||||
|
short uset[8];
|
||||||
|
short rset[8];
|
||||||
|
short aset[8];
|
||||||
|
short qset[8];
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
short byset[8];
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
if (AAtTfp(53, 0, "MAKEDSET>\n"))
|
||||||
|
{
|
||||||
|
pr_set(uset, "uset");
|
||||||
|
pr_set(rset, "rset");
|
||||||
|
pr_set(aset, "aset");
|
||||||
|
pr_set(qset, "qset");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
while (t != (QTREE *) 0)
|
||||||
|
{
|
||||||
|
switch (t->sym.type)
|
||||||
|
{
|
||||||
|
case VAR:
|
||||||
|
if (((VAR_NODE *) t)->varno == vn)
|
||||||
|
lsetbit(((VAR_NODE *) t)->attno, rset);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AGHEAD:
|
||||||
|
/* do protection on qualification */
|
||||||
|
dopro(vn, t, -1, byset);
|
||||||
|
|
||||||
|
/* merge by-list set into qualification set */
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
qset[i] |= byset[i];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BYHEAD:
|
||||||
|
case AOP:
|
||||||
|
AAsyserr(19046, t->sym.type);
|
||||||
|
|
||||||
|
case RESDOM:
|
||||||
|
if (((RES_NODE *) t)->resno == 0)
|
||||||
|
{
|
||||||
|
/* tid -- ignore right subtree (and this node) */
|
||||||
|
t = t->left;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (uset != (short *) 0)
|
||||||
|
lsetbit(((RES_NODE *) t)->resno, uset);
|
||||||
|
/* explicit fall-through to "default" case */
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* handle left subtree (recursively) */
|
||||||
|
makedset(vn, t->left, uset, rset, aset, qset);
|
||||||
|
|
||||||
|
/* handle right subtree (iteratively) */
|
||||||
|
t = t->right;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
if (AAtTfp(53, 15, "<MAKEDSET\n"))
|
||||||
|
{
|
||||||
|
pr_set(uset, "uset");
|
||||||
|
pr_set(rset, "rset");
|
||||||
|
pr_set(aset, "aset");
|
||||||
|
pr_set(qset, "qset");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** PROAPPL -- check for protection tuple applicable
|
||||||
|
**
|
||||||
|
** A given AA_PROTECT catalog tuple is checked in a query-
|
||||||
|
** independent way for applicability.
|
||||||
|
**
|
||||||
|
** This routine checks such environmental constraints as the
|
||||||
|
** user, the terminal, and the time of day. The code is
|
||||||
|
** fairly straightforward, just take a look.
|
||||||
|
**
|
||||||
|
** One note: the user and terminal codes contained in the
|
||||||
|
** AA_PROTECT catalog are blank to mean 'any value' of the
|
||||||
|
** corresponding field.
|
||||||
|
**
|
||||||
|
** Parameters:
|
||||||
|
** protup -- the protection tuple to compare against.
|
||||||
|
**
|
||||||
|
** Returns:
|
||||||
|
** TRUE -- this tuple applies to the current environment.
|
||||||
|
** FALSE -- this tuple does not apply.
|
||||||
|
*/
|
||||||
|
proappl(protup)
|
||||||
|
struct protect *protup;
|
||||||
|
{
|
||||||
|
register struct protect *p;
|
||||||
|
long tvect;
|
||||||
|
register int *tt;
|
||||||
|
extern int *localtime();
|
||||||
|
register int mtime;
|
||||||
|
|
||||||
|
p = protup;
|
||||||
|
|
||||||
|
/* check for correct user [insert clique code here] */
|
||||||
|
if (!AAbequal(" ", p->prouser, 2))
|
||||||
|
{
|
||||||
|
if (!AAbequal(p->prouser, AAusercode, 2))
|
||||||
|
return (FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for correct terminal */
|
||||||
|
if (*p->proterm != ' ')
|
||||||
|
{
|
||||||
|
if (!AAbequal(p->proterm, Terminal, PROTERM))
|
||||||
|
return (FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for correct time of day & week */
|
||||||
|
time(&tvect);
|
||||||
|
# ifdef ESER_VMX
|
||||||
|
tt = localtime(tvect);
|
||||||
|
# else
|
||||||
|
tt = localtime(&tvect);
|
||||||
|
# endif
|
||||||
|
mtime = tt[2] * 60 + tt[1];
|
||||||
|
|
||||||
|
if (p->protodbgn <= p->protodend)
|
||||||
|
{
|
||||||
|
if (p->protodbgn > mtime || mtime > p->protodend)
|
||||||
|
return (FALSE);
|
||||||
|
}
|
||||||
|
else if (p->protodbgn > mtime && mtime > p->protodend)
|
||||||
|
return (FALSE);
|
||||||
|
|
||||||
|
if (p->prodowbgn <= p->prodowend)
|
||||||
|
{
|
||||||
|
if (p->prodowbgn > tt[6] || tt[6] > p->prodowend)
|
||||||
|
return (FALSE);
|
||||||
|
}
|
||||||
|
else if (p->prodowbgn > tt[6] && tt[6] > p->prodowend)
|
||||||
|
return (FALSE);
|
||||||
|
|
||||||
|
/* hasn't failed yet -- I guess it's ok */
|
||||||
|
return (TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** PROCHK -- query-dependent protection tuple check
|
||||||
|
**
|
||||||
|
** This routine does the query-dependent part of checking
|
||||||
|
** the validity of a protection tuple. Unlike proappl,
|
||||||
|
** which looked at aspects of the environment but not the
|
||||||
|
** query being run, this routine assumes that the environ-
|
||||||
|
** ment is ok, and checks that if it applies to this tuple.
|
||||||
|
**
|
||||||
|
** Two things are checked. The first is if this tuple applies
|
||||||
|
** to the operation in question (passed as 'inbit'). The
|
||||||
|
** second is if the set of domains in the tuple contains the
|
||||||
|
** set of domains in the query. If either of these fail,
|
||||||
|
** the return is zero. Otherwise the return is the operation
|
||||||
|
** bit. In otherwise, the return is the operation to which
|
||||||
|
** this tuple applies (if any).
|
||||||
|
**
|
||||||
|
** As a special check, the domain set is checked for all
|
||||||
|
** zero. If so, no domains have been referenced for this
|
||||||
|
** operation at all, and we return zero. In other words, this
|
||||||
|
** tuple might apply to this operation, but since we don't
|
||||||
|
** use the operation anyhow we will ignore it. It is important
|
||||||
|
** to handle things in this way so that the qualification for
|
||||||
|
** this tuple doesn't get appended if the variable is not
|
||||||
|
** used in a particular context.
|
||||||
|
**
|
||||||
|
** Parameters:
|
||||||
|
** inbit -- the bit describing the operation to be
|
||||||
|
** checked. Note that only one bit should
|
||||||
|
** be set in this word, although this is
|
||||||
|
** not checked.
|
||||||
|
** domset -- the set of domains actually referenced
|
||||||
|
** in this query for the operation described
|
||||||
|
** by 'inbit'.
|
||||||
|
** protup -- the tuple in question.
|
||||||
|
**
|
||||||
|
** Returns:
|
||||||
|
** The operation (if any) to which this tuple applies.
|
||||||
|
*/
|
||||||
|
prochk(inbit, domset, protup)
|
||||||
|
int inbit;
|
||||||
|
short domset[8];
|
||||||
|
struct protect *protup;
|
||||||
|
{
|
||||||
|
register struct protect *p;
|
||||||
|
register short *d;
|
||||||
|
register int i;
|
||||||
|
|
||||||
|
p = protup;
|
||||||
|
d = domset;
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
if (AAtTfp(55, 0, "->PROCHK, inbit=0%o, proopset=0%o\n", inbit, p->proopset))
|
||||||
|
{
|
||||||
|
pr_set(d, "domset");
|
||||||
|
pr_set(p->prodomset, "prodomset");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* check for NULL domain set, if so return zero */
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
if (d[i] != 0)
|
||||||
|
break;
|
||||||
|
if (i >= 8)
|
||||||
|
{
|
||||||
|
# ifdef xQTR2
|
||||||
|
AAtTfp(55, 15, "PROCHK-> NULL set\n");
|
||||||
|
# endif
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see if this tuple applies to this operation */
|
||||||
|
if ((inbit & p->proopset) == 0)
|
||||||
|
{
|
||||||
|
# ifdef xQTR2
|
||||||
|
AAtTfp(55, 15, "PROCHK-> no op\n");
|
||||||
|
# endif
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if domains are a subset */
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if ((d[i] & ~p->prodomset[i]) != 0)
|
||||||
|
{
|
||||||
|
/* failure */
|
||||||
|
# ifdef xQTR2
|
||||||
|
AAtTfp(55, 15, "PROCHK-> not subset\n");
|
||||||
|
# endif
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is hereby an "interesting" tuple */
|
||||||
|
# ifdef xQTR2
|
||||||
|
AAtTfp(55, 15, "PROCHK-> %d\n", inbit);
|
||||||
|
# endif
|
||||||
|
return (inbit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ifdef xQTR1
|
||||||
|
/*
|
||||||
|
** PR_SET -- print set for debugging
|
||||||
|
**
|
||||||
|
** This routine prints a 128-bit set for debugging.
|
||||||
|
**
|
||||||
|
** Parameters:
|
||||||
|
** xset -- the set to convert.
|
||||||
|
** labl -- a label to print before the set.
|
||||||
|
*/
|
||||||
|
pr_set(xset, labl)
|
||||||
|
register short *xset;
|
||||||
|
register char *labl;
|
||||||
|
{
|
||||||
|
register int i;
|
||||||
|
|
||||||
|
printf("\t%s: hex/", labl);
|
||||||
|
if (xset == (short *) 0)
|
||||||
|
{
|
||||||
|
printf("NULL/\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
printf("%04x/", xset[i]);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
# endif
|
||||||
Loading…
Reference in New Issue