This commit is contained in:
Oliver Lehmann 1989-07-12 09:56:55 +00:00
parent ab5f16e2a3
commit 2199bf4643
2 changed files with 700 additions and 0 deletions

View File

@ -0,0 +1,316 @@
# include <stdio.h>
# include "gen.h"
# include "../h/dbs.h"
# include "../h/aux.h"
# include "../h/tree.h"
# include "../h/symbol.h"
# include "../h/pipes.h"
# include "decomp.h"
/*
** BYEVAL - process aggregate function
**
** Byeval is passed the root of the original query
** tree and the root of the aggregate function to
** be processed.
**
** It first creates a temporary relation which will
** hold the aggregate result. The format of the relation
** is:
** SYSNAMExxxxxaa(count, by-dom1, ... , by-domn, ag1, ... , agm)
**
** The relation is moved into the range table and will become
** a part of the query.
**
** If there are any occurences of the variables
** from the by-domains, anywhere in the original query tree,
** the aggregate relation is linked on all by-domains in the
** original query tree.
**
** If the aggregate is unique, multivariable, or has a
** qualification, then special processing is done.
**
** If the aggregate is qualified then the by-domains are
** projected into the result relation. This guarantees that
** every value of the by-domains will be represented in the
** aggregate result.
**
** If the aggregate is unique or multivariable, then another
** temporary relation is created and the values which will be
** aggregated; along with the by-domains, are retrieved into
** the temporary relation.
**
** If unique, then duplicates are removed from the temporary relation.
**
** Next the result relation for the aggregate is modified
** to hash in order to speed up the processing of the aggregate
** and guarantee that there are no duplicates in the bylist.
**
** The aggregate is then run, and if a temporary relation was
** created (eg. unique or multivar aggregate) then it is destroyed.
**
*/
QTREE *byeval(root, aghead, agvar)
QTREE *root; /* root of orig query */
QTREE *aghead; /* root of ag fcn sub-tree */
int agvar; /* variable number assigned to this aggregate */
{
register QTREE *q;
register QTREE *ag;
register QTREE *resdom;
register QTREE *r;
int temp_relnum;
int i;
int filled;
QTREE *lnodv[MAXDOM + 2];
QTREE *save_node[MAXDOM + 2];
char agbuf[AGBUFSIZ];
char nums[2];
int relnum;
register QTREE *byhead;
register QTREE **alnp;
int bydoms;
int bymap;
int primeag;
int srcmap;
extern int derror();
extern QTREE *copytree();
extern QTREE *makavar();
extern QTREE *makresdom();
extern QTREE *makroot();
# ifdef xDTR1
AAtTfp(7, -1, "BYEVAL\n");
# endif
ag = aghead;
byhead = ag->left;
/* first create the aggregate result relation */
/* params for create */
initp(); /* init globals for setp */
setp("0"); /* initial relstat field */
relnum = rnum_alloc();
setp(rnum_convert(relnum));
setp("count"); /* domain 1 - count field per BY value */
setp("i4"); /* format of count field */
i = bydoms = lnode(byhead->left, lnodv, 0);
lnodv[i] = 0;
alnp = &lnodv[++i];
i = lnode(byhead->right, lnodv, i);
lnodv[i] = 0;
domnam(lnodv, "by"); /* BY list domains */
domnam(alnp, "ag"); /* aggregate value domains */
call_dbu(mdCREATE, FALSE);
Rangev[agvar].relnum = relnum;
# ifdef xDTR1
AAtTfp(7, 7, "agvar=%d,rel=%s\n", agvar, rnum_convert(relnum));
# endif
bymap = varfind(byhead->left, (QTREE *) 0);
/*
** Find all variables in the tree in which you are nested.
** Do not look at any other aggregates in the tree. Just in
** case the root is an aggregate, explicitly look at its
** two descendents.
*/
srcmap = varfind(root->left, ag) | varfind(root->right, ag);
# ifdef xDTR1
AAtTfp(7, 8, "bymap=0%o,srcmap=0%o\n", bymap, srcmap);
# endif
if (bymap & srcmap)
modqual(root, lnodv, srcmap, agvar);
/* if aggregate is unique or there is a qualification
** or aggregate is multi-var, then special processing is done */
temp_relnum = NORESULT;
filled = FALSE;
primeag = prime(byhead->right);
if (ag->right->sym.type != QLEND || ((ROOT_NODE *) ag)->tvarc > 1 || primeag)
{
/* init a buffer for new tree components */
initbuf(agbuf, AGBUFSIZ, AGBUFFULL, derror);
/* make a root for a new tree */
q = makroot(agbuf);
/*
** Create a RESDOM for each by-domain in the original
** aggregate. Rather than using the existing by-domain
** function, a copy is used instead. This is necessary
** since that subtree might be needed later (if modqual())
** decided to use it. Decomp does not restore the trees
** it uses and thus the by-domains might be altered.
*/
for (i = 0; r = lnodv[i]; i++)
{
resdom = makresdom(agbuf, r);
((RES_NODE *) resdom)->resno = i + 2;
resdom->right = copytree(r->right, agbuf);
resdom->left = q->left;
q->left = resdom;
}
mapvar(q, 0); /* make maps on root */
# ifdef xDTR1
if (AAtTf(7, 2))
printree(q, "by-domains");
# endif
/* if agg is qualified, project by-domains into result */
if (ag->right->sym.type != QLEND)
{
filled = TRUE;
i = Sourcevar; /* save value */
decomp(q, mdRETR, relnum);
Sourcevar = i; /* restore value */
}
/* if agg is prime or multivar, compute into temp rel */
if (((ROOT_NODE *) ag)->tvarc > 1 || primeag)
{
q->right = ag->right; /* give q the qualification */
ag->right = Qle; /* remove qualification from ag */
/* put aop resdoms on tree */
for (i = bydoms + 1; r = lnodv[i]; i++)
{
resdom = makresdom(agbuf, r);
resdom->right = r->right;
resdom->left = q->left;
q->left = resdom;
/* make aop refer to temp relation */
r->right = makavar(resdom, FREEVAR, i);
}
/* assign result domain numbers */
for (resdom = q->left; resdom->sym.type != TREE; resdom = resdom->left)
((RES_NODE *) resdom)->resno = --i;
/*
** change by-list in agg to reference new source rel.
** Save the old bylist to be restored at the end of
** this aggregate.
*/
for (i = 0; resdom = lnodv[i]; i++)
{
save_node[i] = resdom->right;
resdom->right = makavar(resdom, FREEVAR, i + 1);
}
mapvar(q, 0);
# ifdef xDTR1
if (AAtTf(7, 3))
printree(q, "new ag src");
# endif
/* create temp relation */
temp_relnum = mak_t_rel(q, "a", -1);
decomp(q, mdRETR, temp_relnum);
Rangev[FREEVAR].relnum = temp_relnum;
Sourcevar = FREEVAR;
if (primeag)
removedups(FREEVAR);
# ifdef xDTR1
if (AAtTf(7, 4))
printree(ag, "new agg");
# endif
}
}
/* set up parameters for modify to hash */
initp();
setp(rnum_convert(relnum));
setp("hash"); /* modify the empty rel to hash */
setp("num"); /* code to indicate numeric domain names */
nums[1] = '\0';
for (i = 0; i < bydoms; i++)
{
nums[0] = i + 2;
setp(nums);
}
setp("");
/* set up fill factor information */
setp("minpages");
if (filled)
{
setp("1");
setp("fillfactor");
setp("100");
}
else
setp("10");
specclose(relnum);
call_dbu(mdMODIFY, FALSE);
Newq = 1;
Newr = TRUE;
call_ovqp(ag, mdRETR, relnum);
Newq = 0;
/* if temp relation was used, destroy it */
if (temp_relnum != NORESULT)
{
for (i = 0; resdom = lnodv[i]; i++)
resdom->right = save_node[i];
dstr_rel(temp_relnum);
}
}
modqual(root, lnodv, srcmap, agvar)
QTREE *root;
QTREE *lnodv[];
int srcmap;
int agvar;
{
register QTREE *and_eq;
register QTREE *afcn;
register int i;
extern QTREE *copytree();
extern QTREE *makavar();
extern QTREE *need();
# ifdef xDTR1
AAtTfp(14, 5, "modqual 0%o\n", srcmap);
# endif
for (i = 0; afcn = lnodv[i]; i++)
{
/* 'AND' node */
and_eq = need(Qbuf, QT_HDR_SIZ + AND_SIZ);
and_eq->sym.type = AND;
and_eq->sym.len = AND_SIZ;
((ROOT_NODE *) and_eq)->tvarc = 0;
((ROOT_NODE *) and_eq)->lvarc = 0;
((ROOT_NODE *) and_eq)->lvarm = 0;
((ROOT_NODE *) and_eq)->rvarm = 0;
and_eq->right = root->right;
root->right = and_eq;
/* 'EQ' node */
and_eq->left = need(Qbuf, QT_HDR_SIZ + OP_SIZ);
and_eq = and_eq->left;
and_eq->sym.type = BOP;
and_eq->sym.len = OP_SIZ;
((OP_NODE *) and_eq)->opno = (int) opEQ;
/* bydomain opEQ var */
and_eq->right = copytree(afcn->right, Qbuf); /* a-fcn (in Source) specifying BY domain */
and_eq->left = makavar(afcn, agvar, i + 2); /* VAR ref BY domain */
}
}

View File

@ -0,0 +1,384 @@
# include <stdio.h>
# include "gen.h"
# include "../h/dbs.h"
# include "../h/symbol.h"
# include "../h/tree.h"
# include "../h/pipes.h"
# include "ovqp.h"
# include "strategy.h"
int Nsimp; /* Current no. entries in Simp vector */
struct simp Simp[NSIMP];
/*
** Findsimps searches the qualification for
** occurances of simple clauses. In its
** current crude implementation it only finds
** clauses of the form:
**
** var relop constant or constant relop var
**
** it does not use simple clauses with 'OR's
** nor does it accept clauses of the form
**
** var relop constant + constant etc.
**
** Findsimps knows about pattern matching characters
** and treats char constants containing pm chars
** specially. For example
** var >= "xx*" --> var >= "xx"
** var <= "xx*" --> var <= "xx\0177"
** var = "xx*" --> var >= "xx" and var <= "xx\0177"
** If the first char is a pm char then the clause is not
** considered as a simple clause. Also notice that the conversion
** is done only once. If the next time Newq = FALSE, then findsimps()
** isn't called. This works because a pm char can only come from
** the user and not from a relation. Thus during tuple substition
** a constant with a pm will never change.
*/
findsimps()
{
register SYMBOL *c;
register int t;
int length;
register SYMBOL **q;
int attno;
int rel;
int found;
SYMBOL *xc;
extern SYMBOL *cpsym();
# ifdef xOTR1
AAtTfp(21, 0, "FINDSIMPS\n");
# endif
Nsimp = 0;
found = FALSE;
q = Qlist; /* q holds pointer to qualification */
if (!q)
return (0);
for (t = ctou((*q)->type); t != QLEND; t = ctou((*++q)->type))
{
switch (t)
{
case VAR:
attno = ctou(*(*q)->value); /* save att number */
if ((t = ctou((*++q)->type)) == INT
|| t == FLOAT || t == CHAR || t == S_VAR)
{
c = *q; /* save pointer to value symbol */
t = ctou((*++q)->type);
if ((rel = relop(*q, FALSE)) >= 0
&& (t = ctou((*++q)->type)) == AND)
{
/* found a simple clause */
found = TRUE;
}
}
break;
case S_VAR:
case INT:
case FLOAT:
case CHAR:
c = *q++;
if ((t = ctou((*q)->type)) == VAR)
{
attno = ctou(*(*q)->value);
t = ctou((*++q)->type);
if ((rel = relop(*q, TRUE)) >= 0 && (t = ctou((*++q)->type)) == AND)
{
/* found a simple clause */
found = TRUE;
}
}
}
if (found)
{
/* a simple clause has been found.
** Check that the constant contains
** at least one char before any pattern
** matching char. If there is a pattern
** matching char then special processing
** must be done.
*/
found = FALSE;
if (length = check(c))
{
/*
** If length is zero then the first char was
** a pattern matching char. If length < 0 then
** no pattern matching char, and finally
** if length > 0 then length is the number of
** chars before the first pattern matching char
*/
if (length > 0)
{
switch (rel)
{
case opEQ:
/*
** Create two simple clauses:
** One below the value and the
** other above the value.
*/
xc = cpsym(c, length, opLTLE);
add_simp(xc, opLTLE, attno);
rel = (int) opGTGE;
/* fall through to GTGE case */
case opGTGE:
c = cpsym(c, length, opGTGE);
break;
case opLTLE:
c = cpsym(c, length, opLTLE);
break;
}
}
if (add_simp(c, rel, attno))
break; /* no more room in simps */
}
}
while (t != AND) /* skip to next AND */
t = ctou((*++q)->type);
}
# ifdef xOTR1
AAtTfp(21, 2, "FINDSIMPS: rets %d\n", Nsimp);
# endif
return (Nsimp);
}
/*
** RELOP determines whether a symbol is a
** usable relational operator ie. =,>,>=,<,<=
**
** returns the type of the relational
** operator if found, else it returns
** -1
**
** Items are normalized to be in the form:
** var relop constant. If reverse is TRUE then
** complement the sense of the relop. Reverse will
** be TRUE is the simple clause was found in the
** form constant relop var.
*/
relop(s, reverse)
register SYMBOL *s;
register int reverse;
{
register int v;
v = -1; /* assume failure */
if (s->type == BOP)
{
switch (ctou(*s->value))
{
case opEQ:
v = (int) opEQ;
break;
case opLT:
case opLE:
v = (int) opLTLE;
if (reverse)
v = (int) opGTGE;
break;
case opGT:
case opGE:
v = (int) opGTGE;
if (reverse)
v = (int) opLTLE;
break;
}
}
return (v);
}
/*
** CHECK checks the symbol for
** pattern matching characters.
** If any are found then check returns
** the number of characters before the
** first pattern matching character.
**
** If no pattern matching chars are found
** then check returns -1.
**
** note that PAT_RBRAC need not be checked for
** since it is not a pattern matching char unless
** PAT_LBRAC appears before it.
**
** PAT_LBRAC is treated specially in cpsym().
** If any are detected, then length until the
** first PAT_ANY or PAT_ONE is returned.
*/
check(s)
register SYMBOL *s;
{
register char *cp;
register int len;
register int flag;
# ifdef xOTR1
if (AAtTfp(21, 4, "CHECK: sym"))
prsym(s);
# endif
if (s->type == CHAR)
{
flag = FALSE;
cp = (char *) s->value; /* the string is a literal */
len = ctou(s->len);
while (len--)
{
switch (*cp++)
{
case PAT_ANY:
case PAT_ONE:
return ((ctou(s->len)) - len - 1);
case PAT_LBRAC:
flag = TRUE;
}
}
if (flag)
return (ctou(s->len)); /* constant had PAT_LBRAC char */
}
return (-1); /* ok */
}
# ifdef EBCDIC
# define LAST_CHAR 0377
# else
# define LAST_CHAR 0177
# endif
/*
** CPSYM -- copy a symbol to a new buffer area.
** If op is opLTLE then add a pad character
** whose value is the largest possible char
** value.
**
** If any ranges of characters are found,
** then the lowest/highest char is taken from
** range.
*/
SYMBOL *cpsym(konst, len, op)
SYMBOL *konst;
int len;
int op;
{
register SYMBOL *s;
register char *cp;
register int i;
register char *sp;
register int c;
register int nc;
extern char *Ovqpbuf;
extern char *need();
i = len;
len += SYM_HDR_SIZ;
if (op == (int) opLTLE)
len++;
s = (SYMBOL *) need(Ovqpbuf, len);
s->type = CHAR;
sp = (char *) s->value;
cp = (char *) konst->value;
while (i--)
{
/* copy chars processing LBRAC chars if any */
if ((c = *cp++) == PAT_LBRAC)
{
/* if string is empty, ignore it */
if (i == 0)
break;
c = *cp++;
i--;
if (c == PAT_RBRAC)
continue; /* empty [] */
while (i-- && ((nc = *cp++) != PAT_RBRAC))
{
/* ignore '-' */
if (nc == '-')
continue;
/* check for char larger/smaller than 'c' */
if (op == (int) opLTLE)
{
if (nc > c)
c = nc;
}
else
{
if (nc < c)
c = nc;
}
}
}
*sp++ = c; /* copy next char */
}
if (op == (int) opLTLE)
*sp++ = LAST_CHAR;
s->len = sp - (char *) s->value;
return (s);
}
/*
** ADD_SIMP -- add a simple clause to the list of
** simple clauses. As a side effect the Nsimp
** is incremented. If there is no room return
** TRUE else return FALSE
*/
add_simp(konst, rel, attno)
register SYMBOL *konst;
register int rel;
int attno;
{
register struct simp *s;
if (Nsimp == NSIMP)
return (TRUE); /* no more room */
s = &Simp[Nsimp++];
s->att = attno;
s->konst = konst;
s->relop = rel;
# ifdef xOTR1
if (AAtTf(21, 3))
prsimp(s);
# endif
return (FALSE);
}
# ifdef xOTR1
prsimp(s)
register struct simp *s;
{
printf("simp:relop=%d,att=%d,val=", s->relop, s->att);
prsym(s->konst);
}
# endif