329 lines
7.1 KiB
C
329 lines
7.1 KiB
C
# include <stdio.h>
|
|
# include "gen.h"
|
|
# include "../h/dbs.h"
|
|
# include "../h/aux.h"
|
|
# include "../h/symbol.h"
|
|
# include "../h/tree.h"
|
|
# include "../h/pipes.h"
|
|
# include "decomp.h"
|
|
|
|
/*
|
|
** Reduction -- This file contains routines related to the
|
|
** reduction algorithm. The routines are all called by
|
|
** decision().
|
|
**
|
|
** Included are:
|
|
**
|
|
** algorithm -- groups clauses according to common variables
|
|
**
|
|
** buildlist -- build linked list of clauses from a query tree
|
|
**
|
|
** construct -- build query trees from link lists of clauses
|
|
**
|
|
** order -- order a linked list of clauses into the prefered
|
|
** execution sequence
|
|
*/
|
|
|
|
|
|
algorithm(clist, varmap)
|
|
struct complist *clist;
|
|
int varmap;
|
|
|
|
/*
|
|
** Algorithm - determine whether query is connected or not
|
|
**
|
|
** Algorithm takes a linked list of query components
|
|
** and groups them according to the variables involved
|
|
** in each component. If the query is fully interconnected
|
|
** then algorithm returns TRUE else it returns FALSE.
|
|
**
|
|
** By definition a constant clause (one involving zero variables)
|
|
** is considered to use all variables. Without this rule,
|
|
** a query with a null target list would always break into
|
|
** two pieces.
|
|
**
|
|
** Whether a query is fully connected is independent
|
|
** of whether there are any one variable components
|
|
** or not. This includes the target list. After applying
|
|
** the algorithm, a scan is made to see how many multi-var
|
|
** components are present. If there are two or more multi-var
|
|
** components then the query is disconnected.
|
|
*/
|
|
|
|
{
|
|
register struct complist *chead;
|
|
register struct complist *cnext;
|
|
register int var;
|
|
int vmap;
|
|
struct complist *cprev;
|
|
struct complist *cl;
|
|
|
|
vmap = varmap;
|
|
for (var = 1; vmap; var <<= 1)
|
|
{
|
|
if ((vmap & var) == 0)
|
|
continue;
|
|
vmap &= ~var; /* remove var */
|
|
|
|
/* done if query is already a single piece */
|
|
if (clist->nextcomp == 0)
|
|
break;
|
|
|
|
/* find first component using variable var */
|
|
for (chead = clist; chead; chead = chead->nextcomp)
|
|
{
|
|
if (chead->bitmap == 0 || (chead->bitmap & var))
|
|
{
|
|
/* this component uses variable var */
|
|
|
|
cprev = chead;
|
|
|
|
/* look for other components using variable var */
|
|
for (cnext = chead->nextcomp; cnext; cnext = cnext->nextcomp)
|
|
{
|
|
if (cnext->bitmap == 0 || (cnext->bitmap & var))
|
|
{
|
|
|
|
/*
|
|
** Found a component. Remove it from "next"
|
|
** link and put it with head piece
|
|
*/
|
|
|
|
/* remove piece */
|
|
cprev->nextcomp = cnext->nextcomp;
|
|
|
|
/* fix up bit map */
|
|
chead->bitmap |= cnext->bitmap;
|
|
|
|
/* find end of head component */
|
|
for (cl = chead; cl->linkcomp; cl = cl->linkcomp)
|
|
continue;
|
|
|
|
/* connect with head piece */
|
|
cl->linkcomp = cnext;
|
|
}
|
|
else
|
|
cprev = cnext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* return whether connected or not connected */
|
|
for (var =0, chead = clist; chead; chead = chead->nextcomp)
|
|
if (bitcnt(chead->bitmap) > 1)
|
|
var++; /* this component involves 2 or more vars */
|
|
|
|
return (var < 2); /* return TRUE if zero or one component multi-var */
|
|
}
|
|
|
|
|
|
struct complist *buildlist(root1, buf)
|
|
QTREE *root1;
|
|
char *buf;
|
|
|
|
/*
|
|
** Buildlist -- Build a component list from a query tree.
|
|
**
|
|
** Each ROOT and AND node is treated as a separate component
|
|
** and a linked list is built connecting them. The ROOT node
|
|
** MUST be the first element. This list will later be manipulated
|
|
** by algorithm() to determine the structure of the query.
|
|
**
|
|
** Returns:
|
|
** Head of the list
|
|
*/
|
|
|
|
{
|
|
register struct complist *head;
|
|
register struct complist *next;
|
|
register QTREE *root;
|
|
struct complist *ret;
|
|
extern char *need();
|
|
extern QTREE *makroot();
|
|
|
|
ret = (struct complist *) need(buf, 0);
|
|
head = 0;
|
|
|
|
for (root = root1; root->sym.type != QLEND; root = root->right)
|
|
{
|
|
next = (struct complist *) need(buf, sizeof (struct complist));
|
|
next->clause = root;
|
|
next->nextcomp = next->linkcomp = 0;
|
|
next->bitmap = ((ROOT_NODE *) root)->lvarm;
|
|
|
|
if (head)
|
|
head->nextcomp = next;
|
|
head = next;
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
|
|
QTREE *construct(root, listhead, buf)
|
|
QTREE *root;
|
|
struct complist *listhead;
|
|
char *buf;
|
|
/*
|
|
** Construct -- construct a tree from a list component
|
|
**
|
|
** Construct takes a list head and builds a querytree
|
|
** from the components in the list. If the head component
|
|
** is the ROOT of the original query, then
|
|
** the original ROOT node is reused.
|
|
**
|
|
*/
|
|
{
|
|
register QTREE *ret;
|
|
register QTREE *q;
|
|
register struct complist *clist;
|
|
extern QTREE *makroot();
|
|
|
|
clist = listhead;
|
|
|
|
/* determine ROOT of tree */
|
|
if (root == clist->clause)
|
|
{
|
|
q = root;
|
|
clist = clist->linkcomp;
|
|
}
|
|
else
|
|
{
|
|
q = makroot(buf);
|
|
}
|
|
ret = q;
|
|
for ( ; clist; clist = clist->linkcomp)
|
|
{
|
|
q->right = clist->clause;
|
|
q = q->right;
|
|
}
|
|
|
|
q->right = Qle;
|
|
|
|
mapvar(ret, 1);
|
|
# ifdef xDTR1
|
|
if (AAtTf(16, 0))
|
|
printree(ret, "Construct");
|
|
# endif
|
|
return (ret);
|
|
}
|
|
|
|
|
|
|
|
struct complist *order(clist, ovlapvar)
|
|
struct complist *clist;
|
|
int ovlapvar;
|
|
|
|
/*
|
|
** Order -- order a link list set of query components.
|
|
**
|
|
** Takes a list of components and orders them:
|
|
** first - disjoint components
|
|
** second - reduction pieces
|
|
** last - the original target list piece
|
|
**
|
|
** Return:
|
|
** new head of list
|
|
*/
|
|
|
|
{
|
|
register struct complist *cl;
|
|
register struct complist *joint;
|
|
register struct complist *disjoint;
|
|
struct complist *xd;
|
|
struct complist *xj;
|
|
struct complist *tlpiece;
|
|
struct complist *ret;
|
|
QTREE *tmp;
|
|
int map;
|
|
|
|
tlpiece = clist; /* target list piece always first */
|
|
disjoint = joint = 0;
|
|
map = ovlapvar >= 0 ? 1 << ovlapvar : 0;
|
|
|
|
/* examine each irreducible component for disjointness */
|
|
for (cl = tlpiece->nextcomp; cl; cl = cl->nextcomp)
|
|
{
|
|
if (cl->bitmap & map)
|
|
{
|
|
/* joint piece */
|
|
if (joint == 0)
|
|
{
|
|
joint = cl;
|
|
xj = cl;
|
|
}
|
|
else
|
|
{
|
|
joint->nextcomp = cl;
|
|
joint = cl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* disjoint piece */
|
|
if (disjoint == 0)
|
|
{
|
|
disjoint = cl;
|
|
xd = cl;
|
|
}
|
|
else
|
|
{
|
|
disjoint->nextcomp = cl;
|
|
disjoint = cl;
|
|
}
|
|
}
|
|
|
|
}
|
|
/* we now have all disjoint, joint and tl pieces */
|
|
/* order them in order (1) xd, (2) xj, (3) tlpiece */
|
|
|
|
ret = tlpiece;
|
|
tlpiece->nextcomp = 0;
|
|
|
|
if (joint)
|
|
{
|
|
ret = xj;
|
|
joint->nextcomp = tlpiece;
|
|
if ((tlpiece->bitmap & (~map)) == 0)
|
|
{
|
|
/*
|
|
** This is the special case of the target list
|
|
** being one (or zero) variable and that variable
|
|
** is the overlap variable. If left as is, the
|
|
** reduction will take one step more than is
|
|
** necessary. The target list piece is combined
|
|
** with the last joint piece and processed together.
|
|
**
|
|
** An example of when this will happen is:
|
|
** ret(p.a) : p.b = s.b ^ p.c = y.c
|
|
**
|
|
** Reduction would split this up into
|
|
** (1) ret (p.a,p.b) : p.c = y.c
|
|
** (2) ret (p.a) : p.b = s.b
|
|
** (3) ret (p.a)
|
|
**
|
|
** Here we are allowing pieces (2) & (3) to be done together
|
|
*/
|
|
|
|
for (cl = joint; cl->linkcomp; cl = cl->linkcomp)
|
|
continue;
|
|
|
|
cl->linkcomp = tlpiece;
|
|
joint->nextcomp = 0;
|
|
|
|
/* switch tl clause to top of complist */
|
|
tmp = joint->clause;
|
|
joint->clause = tlpiece->clause;
|
|
tlpiece->clause = tmp;
|
|
}
|
|
}
|
|
|
|
if (disjoint)
|
|
{
|
|
ret = xd;
|
|
disjoint->nextcomp = joint ? xj : tlpiece;
|
|
}
|
|
|
|
return (ret);
|
|
}
|