# include	<stdio.h>
# include	<ingres.h>
# include	<pv.h>
# include	<aux.h>
# include	<access.h>
# include	<batch.h>
# include	<lock.h>
# include	<opsys.h>
# include 	<func.h>
# include	<version.h>
# include	<symbol.h>
# include	<catalog.h>
# include	<btree.h>
# include	<sccs.h>
# include	<errors.h>

SCCSID(@(#)modify.c	8.8	5/7/85)

extern	short	tTdbu[];
int	modify();
extern 	void	null_fn();

struct fn_def ModifyFn =
{
	"MODIFY",
	modify,
	null_fn,
	null_fn,
	NULL,
	0,
	tTdbu,
	100,
	'Z',
	0
};

/*
**  MODIFY -- converts any relation to the specified
**		storage structure
**
**	arguments:
**	0 - relation name
**	1 - storage structure ("heap", "cheap", "hash", "chash",
**		"isam", "cisam")
**	2 - "name" for attribute names, or "num" for numbers
**	3 - key1
**	4 - key2
**	    .
**	    .
**	i - null
**	i+1 - option name (e.g., "fillfactor")
**	i+2 - option value
**	    .
**	    .
**
**	If all the options default, parameter i -> pc are omitted.
**	If no keys are provided, parameter 2 is omitted.
*/

int		F_fac, Mn_pages, Mx_pages;
char		Lid[MAXLID][MAXNAME];
int		NLidKeys;
int		LidKey[MAXLID];

struct modtab
{
	char	*type;
	char	newrelspec;
	char	yeskeys;
	char	sortit;
	char	yes_seq;
	int	f_fac;
	int	mn_pages;
	int	mx_pages;
};


struct modtab	Modtab[] =
{
	/* type		spec	keys	sort	seq	ffac	min	max */

	"heap",		M_HEAP,	FALSE,	FALSE,	FALSE,	0,	0,	0,
	"cheap",	-M_HEAP,FALSE,	FALSE,	FALSE,	0,	0,	0,
	"hash",		M_HASH,	TRUE,	TRUE,	FALSE,	50,	10,	-1,
	"chash",	-M_HASH,TRUE,	TRUE,	FALSE,	75,	1,	-1,
	"isam",		M_ISAM,	TRUE,	TRUE,	FALSE,	80,	0,	0,
	"cisam",	-M_ISAM,TRUE,	TRUE,	FALSE,	100,	0,	0,
	"heapsort",	M_HEAP,	TRUE,	TRUE,	TRUE,	0,	0,	0,
	"cheapsort",	-M_HEAP,TRUE,	TRUE,	TRUE,	0,	0,	0,
	"truncated",	M_TRUNC,FALSE,	FALSE,	FALSE,	0,	0,	0,
	"ordered",	M_ORDER,TRUE,	FALSE,	FALSE,	0,	0,	0,
	0
};

struct mod_info
{
	char	outfile[MAXNAME + 4];	/* result file filled by ksort */
	char	formfile[MAXNAME + 4];	/* file with descriptor for ksort */
	char	infile[MAXNAME + 4];	/* input file for ksort (relation itself */
	char	reltemp[MAXNAME + 4];	/* file holding new relation */
	char	spfile[MAXNAME + 4], spflag;	/* isam spool file for overflow */
	char	btree[MAXNAME + 4];	/* file holding temporary btree structure */
	char	temp_sort[MAXNAME + 4];	/* file holding result of special isam
					** required when ordering on a field
					*/
};

struct mod_info	Mod_info;

extern DESC Btreesec;
extern int Btree_fd;



modify(pc, pv)
int	pc;
PARM	*pv;
{
	register int		i, j;
	register char		*rname;
	register struct modtab	*mp;
	struct modtab		*p;
	int			sorted, dim;
	DESC			dold, dnew;
	long			temptid;
	extern int		Noupdt;
	extern DESC		Attdes;
	struct attribute	atttup, attkey;
	TID			tid;
	int			lidkey, numatts;
	extern char		*trim_relname();
	extern char		*iocv();

	
#	ifdef xZTR1
	if (tTf(34, -1))
	{
		printf("enter modify\n");
		prvect(pc, pv);
	}
#	endif

	pv[pc].pv_val.pv_str = NULL;

	/* check for nice parameters */
	if (pc < 2)
		syserr("MODIFY: pc %d", pc);

	/* save relation name for error messages */
	rname = (pv++)->pv_val.pv_str;	/* *pv now pointes to storage spec */

	/* check for good relation */
	i = openr(&dold, OR_READ, rname);
	if (i == AMOPNVIEW_ERR)
		return (error(NOMODVIEW, rname, 0));
	if (i > 0)
		/* reln does not exist */
		return (error(NOREL, rname, 0));	
	else if (i < 0)
		syserr("MODIFY: openr (%.14s) %d", rname, i);
	/* can only modify a relation you own and isn't a sys rel */
	
	if (!bequal(Usercode, dold.reldum.relowner, UCODE_SZ))
	{
		i = NOOWN;
	}
	if ((dold.reldum.relstat & S_CATALOG) && Noupdt)
	{
		i = NOMODSYSREL;
	}
	if (i)
	{
		closer(&dold);
		return (error(i, rname, 0));
	}

	/*
	** Form descriptor for new relation. Here we need to
	** separate the pages from the old and new relations.
	** Since pages are identified by the TID of the relation
	** relation tuple, both old and new have the same identifiers.
	** To avoid this problem, a special TID is hand crafted for
	** the new relation.
	*/
	bmove(&dold, &dnew, sizeof dnew);
	dnew.reltid.s_tupid.line_id = (char) -2;	/* choose impossible reltid */
	/* assume new relation isn't ordered */
	if (dold.reldum.reldim)
	{
		dnew.reldum.relatts -= dold.reldum.reldim;
		dnew.reldum.relwid -= dold.reldum.reldim * LIDSIZE;
		dnew.reldum.reldim = 0;
	}

	/* In case of an interrupt from a previous modify,
	** there might be pages around. Get rid of them.
	*/
	cleanrel(&dnew);

	ingresname(dold.reldum.relid, dold.reldum.relowner, Mod_info.infile);
	dim = 0;
	NLidKeys = 0;

	/* scan for entry in relspec table */
	for (mp = Modtab; mp->type; mp++)
	{
		if (bequal(mp->type, pv->pv_val.pv_str, 7) && bequal("ordered", pv->pv_val.pv_str, 7))
		{
			if ((dim = atoi(pv->pv_val.pv_str + 7)) <= 0 || dim > MAXLID)
			{
				closer(&dold);
				return(error(BADORDDIM, rname, iocv(dim), 0));
			}
			break;
		}
		if (sequal(mp->type, pv->pv_val.pv_str))
			break;
	}

	/* if not found, error */
	if (!mp->type)
	{
		closer(&dold);
		return (error(BADSTORAGE, rname, pv->pv_val.pv_str, 0));	/* bad relspec */
	}

	if (mp->newrelspec == M_ORDER && dold.reldum.relindxd == SECINDEX)
	/* can't order an index relation */
	{
		closer(&dold);
		return(error(NOORDINDX, rname,0));
	}

	if (mp->newrelspec == M_ORDER)
	{
		dnew.reldum.reldim = dim;
		for (i = 0; i < dim; ++i)
		{
			++dnew.reldum.relatts;
			dnew.relxtra[dnew.reldum.relatts] = 0;
			dnew.reloff[dnew.reldum.relatts] = dnew.reldum.relwid;
			dnew.relfrmt[dnew.reldum.relatts] = INT;
			dnew.relfrml[dnew.reldum.relatts] = LIDSIZE;
			dnew.reldum.relwid += LIDSIZE;
		}
		concat(BTREE, Fileset, Mod_info.btree);
		create_btree(Mod_info.btree);
		dnew.btree_fd = Btree_fd;
		/* ok to order ascending/descending */
		mp->yes_seq = TRUE;
	}
	else
	{
		dnew.reldum.relspec = mp->newrelspec;
		if (dold.reldum.reldim)
		{
			dold.reldum.relatts -= dold.reldum.reldim;
			dold.reldum.relwid -= dold.reldum.reldim * LIDSIZE;
			dold.reldum.reldim = 0;
			closer(dold.relbtree);
			close(dold.btree_fd);
		}
	}

	if (dnew.reldum.relspec == M_TRUNC)
		dnew.reldum.relspec = M_HEAP;

	pv++;	/* now points to first parameter */

	/* get the key domains information */
	if ((i = getkeys(&pv, rname, &dnew, mp)) > 0)
	{
		closer(&dold);
		return (i);	/* user error */
	}

	j = 0;
	for (i = 0; i < NLidKeys; ++i)
		if (LidKey[i] > dold.reldum.relatts - dold.reldum.reldim)
		{
			j = 1;
			break;
		}

	if (!j && dold.reldum.reldim)
	/* treat old relation as if not ordered since lid field not needed */
	{
		dold.reldum.relatts -= dold.reldum.reldim;
		dold.reldum.relwid -= dold.reldum.reldim * LIDSIZE;
		dold.reldum.reldim = 0;
		closer(dold.relbtree);
		close(dold.btree_fd);
	}

	if (!dnew.reldum.reldim || !NLidKeys)
	{
		F_fac = mp->f_fac;
		Mn_pages = mp->mn_pages;
		Mx_pages = mp->mx_pages;
	}
	else
	/* set parameters to that of storage type of relation to be ordered */
	{
		for (p = Modtab; p->type; p++)
			if (dnew.reldum.relspec == p->newrelspec)
				break;
		F_fac = p->f_fac;
		Mn_pages = p->mn_pages;
		Mx_pages = p->mx_pages;
	}

	if (mp->newrelspec != M_ORDER)
		for (i = 0; i < dnew.reldum.reldim; ++i)
			Lid[i][0] = NULL;
	else
		for (i = 1; i <= dnew.reldum.reldim; ++i)
			concat("lid", iocv(i), Lid[i-1]);

	/* get fillfactor and other options if any */
	if (i = getfill(&dnew, pv, rname, mp))
	{
		closer(&dold);
		return (i);	/* user error */
	}

	/* check for duplicate attribute name */
	if (mp->newrelspec == M_ORDER)
	{
		opencatalog("attribute", OR_READ);
		setkey(&Attdes, &attkey, dnew.reldum.relid, ATTRELID);
		setkey(&Attdes, &attkey, dnew.reldum.relowner, ATTOWNER);
		numatts = dold.reldum.relatts - dold.reldum.reldim;
		for (i = 0; i < dnew.reldum.reldim; ++i)
		{
			setkey(&Attdes, &attkey, Lid[i], ATTNAME);
			if (getequal(&Attdes, &attkey, &atttup, &tid) == 0)
			{
				if (atttup.attid <= numatts)
				/* ok to duplicate attributes that will be removed */
				{
					closer(&dold);
					return(error(INVALIDATTR, rname, Lid[i], 0));
				}
			}
		}
	}

	/* lock the relation relation */
	if (Lockrel)
	{
		get_p_tid(&dold, &temptid);
		setrll(A_SLP, temptid, M_EXCL);
	}

	if (!dnew.reldum.reldim || NLidKeys > 0)
		/* compute new relation parameters & build descriptor */
		make_newrel(&dnew);

	if (sorted = ((mp->sortit || NLidKeys > 0) && (dold.reldum.reltups != 0)))
	{
		sortrel(&dold, &dnew);
		dold.reldum.relindxd = 0;
	}

	if (!dnew.reldum.reldim || NLidKeys > 0)
		/* physically create the new relation */
		if (formatpg(&dnew, dnew.reldum.relprim) != 0)
			syserr("modify: formatpg");

	/* clear relgiven field; if heap remove any keys */
	clearkeys(&dnew);

	if (abs(dnew.reldum.relspec) == M_HEAP)
		for (i = 1; i <= dnew.reldum.relatts; i++)
			dnew.relxtra[i] = 0;

	if (NLidKeys > 0 && dnew.reldum.relspec == M_ISAM)
		sort_isam(&dold, &dnew);

	if (mp->newrelspec != M_TRUNC)
		fill_rel(&dold, &dnew, sorted);

	closer(&dold);	/* error return is impossible */
	if (abs(dnew.reldum.relspec) == M_ISAM && (!dnew.reldum.reldim || NLidKeys > 0))
	{
		j = dnew.reldum.reldim;
		dnew.reldum.reldim = 0;
		if (i = bldindex(&dnew))
			syserr("bldindex: %.14s %d", dnew.reldum.relid, i);
		dnew.reldum.reldim = j;
		unspool(&dold, &dnew);
	}

	/*
	** New relation is now complete. The system relations need to
	** be updated. First destroy all buffers with pages from the
	** new relation.
	*/
	if (i = cleanrel(&dnew))
		syserr("modify:clean new %d,%.14s", i, dnew.reldum.relid);

	fill_batch(&dold, &dnew);

	/*
	** Close the file for the new relation. This must be
	** done after the fill_batch in case we are modifing
	** the attribute relation.
	*/
	if (!dnew.reldum.reldim || NLidKeys > 0)
		close(dnew.relfp);
	dnew.relopn = 0;
	ruboff("modify");
	modupdate();
	if (mp->newrelspec == M_ORDER)
	{
		close(dnew.btree_fd);
		make_bsec(dnew.reldum.relid, dim);
	}
	rubon();

	if (Lockrel)
		unlrl(temptid);

	return (0);
}


/*
**	GETKEYS - get key domains information
**
**	Parameters:
**		ppv - parameter vector with info about keys
**		relname - relation name
**		d - new descriptor for the relation
**		mp - mod table
**
**	Return Codes:
**		0 - ok
**		>0 - error from modseqkey		
*/
getkeys(ppv, relname, d, mp)
PARM		**ppv;
char		*relname;
register DESC	*d;
struct modtab	*mp;
{
	register PARM		*pv;
	register char		*cp;
	int			namemode, sort_only, as_ds;
	int			i, j, keyno, keywid;
	struct attribute	attkey, atttup;
	struct index		ikey, itup;
	TID			tid;
	extern DESC		Attdes, Inddes;
	extern char		*iocv();

	pv = *ppv;	/* copy list of params */

	if (mp->newrelspec != M_ORDER)
		/* zero key info (ordering does not change keyed fields) */
		for (i = 0; i <= d->reldum.relatts; i++)
			d->relxtra[i] = 0;
	for (i = 0; i <= d->reldum.relatts; ++i)
		d->relgiven[i] = 0;

	/* determine whether there are any keys at all */
	keywid = 0;
	keyno = 0;
	sort_only = FALSE;
	cp = pv->pv_val.pv_str;

	if (cp == NULL || *cp == NULL)
	{
		/* no key information. default as needed */
		if (mp->yeskeys && mp->newrelspec != M_ORDER)
		{
			cp = "\1";	/* default to first key */
			namemode = FALSE;
		}
		else
			pv++;	/* point one to far */
	}
	else
	{
		/* check for name mode */
		if (namemode = sequal(cp, "name"))
		{

			/* check attribute names, and convert them to numbers */
			opencatalog("attribute", OR_READ);
			setkey(&Attdes, &attkey, Mod_info.infile, ATTRELID);
			setkey(&Attdes, &attkey, Usercode, ATTOWNER);
		}
		pv++;	/* inc to next key */
		cp = (pv++)->pv_val.pv_str;
	}

	/* scan for attribute names */
	for (; cp != NULL; cp = (pv++)->pv_val.pv_str)
	{
		/* check for separator between keys & options */
		if (*cp == NULL)
		{
			pv++;	/* point two past NULL */
			break;
		}

		if (NLidKeys >= d->reldum.reldim && mp->newrelspec == M_ORDER)
		{
			/* more than one field specified as ordering key */
			closeall(0l, 0l);
			return(error(TOOMANYORDKEYS, relname, 0));
		}

		if (namemode)
		{
			/* check for "sort only" attribute */
			if (*cp == '#')
			{
				cp++;	/* inc to start of name */
				sort_only = TRUE;
			}

			/* check for ascending/descending modifier */
			if ((as_ds = modseqkey(cp, relname, mp->yes_seq)) > 0)
				return (as_ds);	/* error */

			setkey(&Attdes, &attkey, cp, ATTNAME);
			i = getequal(&Attdes, &attkey, &atttup, &tid);
			if (i < 0)
				syserr("MODIFY: geteq(att) %d", i);
			if (i > 0)
			{
				closeall(0l, 0l);
				return (error(INVALIDATTR, relname, cp, 0));	/* bad att name */
			}
			i = atttup.attid;
			if (i > d->reldum.relatts)
			{
				/* attempting to key on lid field which will be
				** removed
				*/
				closeall(0l,0l);
				return(error(ATTRREMV, relname, cp, 0));
			}
		}
		else
		{
			i = *cp;
			as_ds = 0;
		}

		keyno++;
		/* add new key to descriptor */
		if (mp->newrelspec == M_ORDER)
			LidKey[NLidKeys++] = i;
		if (!sort_only && mp->newrelspec != M_ORDER)
		{
			d->relxtra[i] = keyno;
			keywid += (d->relfrml[i] & I1MASK);
		}
		if (d->relgiven[i])
		{
			closeall(0l, 0l);
			return (error(DUPKEY, relname, cp, 0));	/* duplicate attribute */
		}
		d->relgiven[i] = as_ds == 0 ? keyno : -keyno;
	}
	pv--;	/* back up one to point to "-1" terminator */


	if (abs(d->reldum.relspec) == M_ISAM && keywid > (MAXTUP / 2 - 4))
	{
		closeall(0l, 0l);
		return (error(TOOWIDEISAM, relname, iocv(keywid), 0));
	}

	/* if a heap, there can be no keys */
	if (!mp->yeskeys && keyno != 0)
	{
		closeall(0l, 0l);
		return (error(NOKEYSHEAP, relname, mp->type, 0));	/* no keys allowed on heap */
	}
	/* fill out default sort on remainder of keys */
	if (mp->yeskeys)
		for (i = 1; i <= d->reldum.relatts; i++)
			if (d->relgiven[i] == 0)
				d->relgiven[i] = ++keyno;
	*ppv = pv;
	return (0);
}


/*
**	MODSEQKEY - verify that sequence specified is valid
**
**	Parameters:
**		domain - list of domains
**		relname - relation name
**		seq_ok - whether it is ok to specify the sequence
**				ascending or descending
**
**	Return Codes:
**		0 - ok
**		> 0 - error in sequence specified
**
**	Called by:
**		getkeys
*/
modseqkey(domain, relname, seq_ok)
char	*domain;
char	*relname;
int	seq_ok;
{
	register char	*cp, c;
	register int	ret;

	ret = 0;

	for (cp = domain; c = *cp++; )
		if (c == ':')
			break;

	if (c != '\0')
	{
		/* replace ":" with null */
		*(cp - 1) = '\0';

		/* verify sequence is valid */
		if (!seq_ok)
		{
			closeall(0l, 0l);
			ret = error(BADSEQSPEC, relname, cp, domain, 0);
		}
		else if (sequal("descending", cp) || sequal("d", cp))
			ret = -1;
		else if (!(sequal("ascending", cp) || sequal("a", cp)))
		{
			closeall(0l, 0l);
			ret = error(INVALIDSEQ, relname, cp, domain, 0);
		}
	}

	return (ret);
}
/*
**	GETFILL -- Get fill factor and minimum pages parameters
**		from argument list, convert them from ascii to integer
**		and store them in global variables.  If the global
**		variable for the corresponding parameter is zero,
**		it means that that parameter is not allowed and an
**		error is generated.
*/

/*ARGSUSED*/
getfill(d, pv, rel, mp)
DESC		*d;
register PARM	*pv;
char		*rel;
struct modtab	*mp;
{
	register char		*p1;
	register int		err;
	char			*p2;
	int			i, j;
	int			fill_flag, min_flag, max_flag, lid_flag[MAXLID];

	err = 0;
	fill_flag = min_flag = max_flag = FALSE;
	for (i = 0; i < d->reldum.reldim; ++i)
		lid_flag[i] = FALSE;

	while ((p1 = (pv++)->pv_val.pv_str) != NULL)
	{
		p2 = (pv++)->pv_val.pv_str;
		if (sequal(p1, "fillfactor"))
		{
			if (F_fac == 0 || fill_flag)
			{
				err = NOTALLOWED;
				break;
			}
			p1 = p2;
			F_fac = atoi(p1);
			if (F_fac > 100 || F_fac < 1)
			{
				err = FILLBOUND;
				break;
			}
			fill_flag = TRUE;
			continue;
		}
		if (sequal(p1, "minpages"))
		{
			if (Mn_pages == 0 || min_flag)
			{
				err = NOTALLOWED;
				break;
			}
			p1 = p2;
			Mn_pages = atoi(p1);
			if (Mn_pages < 1)
			{
				err = MINPGBOUND;
				break;
			}
			if (max_flag && (Mn_pages > Mx_pages))
			{
				err = MINGTMAX;
				break;
			}
			min_flag = TRUE;
			continue;
		}
		if (sequal(p1, "maxpages"))
		{
			if (Mx_pages == 0 || max_flag)
			{
				err = NOTALLOWED;
				break;
			}
			p1 = p2;
			Mx_pages = atoi(p1);
			if (Mx_pages < 1)
			{
				err = MAXPGBOUND;
				break;
			}
			if (min_flag && (Mn_pages > Mx_pages))
			{
				err = MINGTMAX;
				break;
			}
			max_flag = TRUE;
			continue;
		}
		for ( i  = 1; i <= d->reldum.reldim && !err; ++i)
			if (sequal(p1, ztack("lid", iocv(i))))
			{
				if (lid_flag[i-1] || *Lid[i-1] == NULL)
				{
					err = NOTALLOWED;
					break;
				}
				for (j = 0; j < d->reldum.reldim; ++j)
					if (i - 1 != j && sequal(p2, Lid[j]) && lid_flag[j])
					{
						err = NOTALLOWED;
						break;
					}
				p1 = p2;
				smove(p1, Lid[i - 1]);
				lid_flag[i - 1] = TRUE;
				break;
			}
			if (err)
				break;
		if (i <= d->reldum.reldim)
			continue;
		err = NEEDFILL;
		break;
	}
	if (err)
	{
		closeall(0l, 0l);
		return (error(err, rel, p1, 0));
	}
	return (0);
}
/*
**  MAKE_NEWREL -- Create a file for the modified relation
**	and build one or more primary pages for the
**	relation based on its storage structure and the
**	number of tuples it must hold.
*/

make_newrel(desc)
register DESC	*desc;
{
	register int	tups_p_page;
	int		width;

	concat(MODTEMP, Fileset, Mod_info.reltemp);
	close(creat(Mod_info.reltemp, FILEMODE));
	if ((desc->relfp = open(Mod_info.reltemp, O_RDWR)) < 0)
		syserr("MAKE_NEWREL: open %.14s %d", Mod_info.reltemp, desc->relfp);
	desc->relopn = (desc->relfp + 1) * -5;
	desc->reldum.relprim = 1;
	if (abs(desc->reldum.relspec) == M_HASH && F_fac > 0 && Mn_pages > 0)
	{
		/*
		** Determine the number of primary pages. The following
		** first determines the number of tuples/page which the
		** relation should have in order to get the requested
		** fillfactor. Then that number is divided into the
		** number of tuples to get the number of primary pages.
		** To avoid round off, it must guaranteed that the
		** number of tuples per page must be at least 1.
		**
		** primary_pages = #tuples / (#tuples/page * fillfactor)
		*/
		width = desc->reldum.relwid + 2 - LIDSIZE * desc->reldum.reldim;
		tups_p_page = (((MAXTUP+2) / width) * F_fac) / 100;
		if (tups_p_page == 0)
			tups_p_page = 1;
		 /* we add one to simulate a ceiling function */
		desc->reldum.relprim = desc->reldum.reltups / tups_p_page + 1;
		if (desc->reldum.relprim < Mn_pages)
			desc->reldum.relprim = Mn_pages;
		if (Mx_pages > 0 && desc->reldum.relprim > Mx_pages)
			desc->reldum.relprim = Mx_pages;
#		ifdef xZTR1
		if (tTf(36, 0))
			printf("using %ld prim pages\n", desc->reldum.relprim);
#		endif
	}
	desc->reldum.reltups = 0;
	return (0);
}
/*
**	SORTREL - Call KSORT to sort the given relation.  SORTREL
**		sets up the descriptor struct specifying the sort
**		keys and tells KSORT whether or not the hash key should
**		be included as a sort key.
*/

sortrel(odesc, desc)
DESC		*odesc;
register DESC	*desc;
{
	extern char	*Pathname;
	register int	i;
	char		buf[50];
	DESC		tempdesc;
	char		*temp;
	int		len;
	short		smalli;

	concat(ISAM_SORTED, Fileset, Mod_info.outfile);
	if (close(creat(Mod_info.outfile, FILEMODE)))
		syserr("SORTREL: creat %.14s", Mod_info.outfile);
	bmove(odesc, &tempdesc, sizeof *odesc);
	for (i = 1; i <= desc->reldum.relatts; ++i)
	/* set up temporary descriptor for ksort with new keyed fields */
	{
		tempdesc.relxtra[i] = desc->relxtra[i];
		tempdesc.relgiven[i] = desc->relgiven[i];
	}

	if (abs(desc->reldum.relspec) == M_HASH && !desc->reldum.reldim)
	{
		/* sort on hash bucket first, (if ordering sort on ordering key, not bucket) */
		tempdesc.relgiven[0] = 1;
		for (i = 1; i <= desc->reldum.relatts; i++)
			tempdesc.relgiven[i]++;
	}

# ifdef xZTR2
	if (tTf(36, 4))
	{
		printf("sortrel: ");
		printdesc(&tempdesc);
	}
# endif

	/* flush buffers used by modify so that ksort can't look at them */
	flush_rel(desc, TRUE);
	resetacc(NULL);

	/* copy Fileset so it can't get trashed */

	len = length(Fileset) + 1;
	temp = (char *) need(Qbuf, len);
	bmove(Fileset, temp, len);

	initp();
	setp(PV_STR, temp);
	setp(PV_STR, Mod_info.infile);
	setp(PV_STR, Mod_info.outfile);

	/* Descriptor for new relation */
	setp(PV_STR, tempdesc.reldum.relid);
	setp(PV_STR, tempdesc.reldum.relowner);
	setp(PV_INT, tempdesc.reldum.relspec);
	setp(PV_INT, tempdesc.reldum.relindxd);
	setp(PV_INT, tempdesc.reldum.relstat2);
	setp(PV_INT, tempdesc.reldum.relstat);
	setp(PV_INT, (short) tempdesc.reldum.relsave);
	setp(PV_INT, (short) tempdesc.reldum.reltups);
	setp(PV_INT, tempdesc.reldum.relatts);
	setp(PV_INT, tempdesc.reldum.relwid);
	setp(PV_INT, (short) tempdesc.reldum.relprim);
	setp(PV_INT, (short) tempdesc.reldum.relfree);
	setp(PV_INT, (short) tempdesc.reldum.relstamp);
	setp(PV_INT, tempdesc.reldum.reldim);

	setp(PV_STR, tempdesc.relvname);
	setp(PV_INT, tempdesc.relfp);
	setp(PV_INT, tempdesc.relopn);
	setp(PV_INT, (short) tempdesc.reladds);
	setp(PV_INT, tempdesc.reltid.ltid);
	for (i = 0; i <= tempdesc.reldum.relatts; ++i)
	{
		smalli = (short) tempdesc.reloff[i];
		setp(PV_INT, smalli);
		smalli = (short) tempdesc.relfrmt[i];
		setp(PV_INT, smalli);
		smalli = (short) tempdesc.relfrml[i];
		setp(PV_INT, smalli);
		smalli = (short) tempdesc.relxtra[i];
		setp(PV_INT, smalli);
		smalli = (short) tempdesc.relgiven[i];
		setp(PV_INT, smalli);
	}

	if (tempdesc.reldum.reldim > 0)
	{
		setp(PV_STR, odesc->relbtree->reldum.relid);
		setp(PV_STR, odesc->relbtree->reldum.relowner);
		setp(PV_INT, odesc->relbtree->reldum.relspec);
		setp(PV_INT, odesc->relbtree->reldum.relindxd);
		setp(PV_INT, odesc->relbtree->reldum.relstat2);
		setp(PV_INT, odesc->relbtree->reldum.relstat);
		setp(PV_INT, (short) odesc->relbtree->reldum.relsave);
		setp(PV_INT, (short) odesc->relbtree->reldum.reltups);
		setp(PV_INT, odesc->relbtree->reldum.relatts);
		setp(PV_INT, odesc->relbtree->reldum.relwid);
		setp(PV_INT, (short) odesc->relbtree->reldum.relprim);
		setp(PV_INT, (short) odesc->relbtree->reldum.relfree);
		setp(PV_INT, (short) odesc->relbtree->reldum.relstamp);
		setp(PV_INT, odesc->relbtree->reldum.reldim);

		setp(PV_STR, odesc->relbtree->relvname);
		setp(PV_INT, odesc->relbtree->relfp);
		setp(PV_INT, odesc->relbtree->relopn);
		setp(PV_INT, (short) odesc->relbtree->reladds);
		setp(PV_INT, odesc->relbtree->reltid.ltid);

		for (i = 0; i <= odesc->relbtree->reldum.relatts; ++i)
		{
			smalli = (short) odesc->relbtree->reloff[i];
			setp(PV_INT, smalli);
			smalli = (short) odesc->relbtree->relfrmt[i];
			setp(PV_INT, smalli);
			smalli = (short) odesc->relbtree->relfrml[i];
			setp(PV_INT, smalli);
			smalli = (short) odesc->relbtree->relxtra[i];
			setp(PV_INT, smalli);
			smalli = (short) odesc->relbtree->relgiven[i];
			setp(PV_INT, smalli);
		}
	}

	call(mdKSORT, NULL);

	/* flush buffers used by ksort so that modify can't look at them */
	flush_rel(desc, TRUE);
	resetacc(NULL);

# ifdef xZTR1
	if (tTf(36,9))
		printf("SORTREL: done calling ksort\n");
#endif
	return (0);
}
/*
**	SORT_ISAM -- Sorts an isam relation back to its original order
**	so that it will be inserted into the relation in the proper order.
**	It is presently not in order because it has been sorted according
**	to a specified field for ordering.
*/
sort_isam(sdesc, desc)
DESC *sdesc;
DESC *desc;
{
	long		lid[MAXLID];
	register int	i, j, k;
	char		tup_buf[MAXTUP], last_tup[MAXTUP], *dp, *sp;
	FILE		*sfp, *fp;
	TID		tid, tidpos;
	DESC		tempdesc;
	int		w;

	if (desc->reldum.reldim > 0)
		Btree_fd = desc->btree_fd;
	concat(STEMP, Fileset, Mod_info.temp_sort);
	if ((sfp = fopen(Mod_info.temp_sort, "w")) == NULL)
		syserr("sort_isam: can't open %s", Mod_info.temp_sort);
	if ((fp = fopen(Mod_info.outfile, "r")) == NULL)
		syserr("sort_isam: can't open %s", Mod_info.outfile);
	for (i = 0; i < desc->reldum.reldim; lid[i++] = 0);
	/* create input file for sort with proper lid attached to each tuple */
	w = sdesc->reldum.relwid - LIDSIZE * sdesc->reldum.reldim;
	for ( ; ; )
	{
		i = fread(tup_buf, 1, sdesc->reldum.relwid, fp);
		if (i == 0)
			break;
		if (i != sdesc->reldum.relwid)
			syserr("sort_isam: read error in %s", Mod_info.outfile);
		for (j = 0; j < desc->reldum.reldim; ++j)
			if (j < NLidKeys && j < desc->reldum.reldim - 1)
			{
				dp = tup_buf + (sdesc->reloff[LidKey[j]] & I1MASK);
				sp = last_tup + (sdesc->reloff[LidKey[j]] & I1MASK);
				if (!bequal(dp, sp, sdesc->relfrml[LidKey[j]]) || !lid[j])
				{
					++lid[j];
					for (k = j + 1; k < desc->reldum.reldim; ++k)
						lid[k] = 0;
					break;
				}
			}
			else
			{
				if (!lid[0])
				{
					lid[0] = 1;
					if (!(desc->reldum.reldim - 1))
						break;
				}
				++lid[desc->reldum.reldim - 1];
				break;
			}
		bmove(tup_buf, last_tup, sdesc->reldum.relwid);
		/* reserve a slot in btree for tuple */
		insert_mbtree(desc, Mod_info.btree, lid, &tid, &tidpos);
		bmove(lid, tup_buf + w, LIDSIZE * desc->reldum.reldim);
		if (fwrite(tup_buf, 1, sdesc->reldum.relwid + LIDSIZE * desc->reldum.reldim, sfp) != sdesc->reldum.relwid + LIDSIZE * desc->reldum.reldim)
			syserr("sort_isam: write error in %s", Mod_info.temp_sort);
	}
	fclose(fp);
	fclose(sfp);
	/* set up new descriptor accounting for lid field */
	bmove(sdesc, &tempdesc, sizeof *sdesc);
	tempdesc.reldum.relspec = M_ORDER;
	for (i = 0; i < desc->reldum.reldim; ++i)
	{
		tempdesc.reldum.relwid += LIDSIZE;
		++tempdesc.reldum.relatts;
		tempdesc.reloff[tempdesc.reldum.relatts] = tempdesc.reldum.relwid - LIDSIZE;
		tempdesc.relfrmt[tempdesc.reldum.relatts] = INT;
		tempdesc.relfrml[tempdesc.reldum.relatts] = LIDSIZE;
	}
	j = 0;
	/* use old keying attributes for specifying sort order */
	clearkeys(&tempdesc);
	for (i = 1; i <= sdesc->reldum.relatts; ++i)
		if (sdesc->relxtra[i])
		{
			tempdesc.relgiven[i] = sdesc->relxtra[i];
			++j;
		}
	for (i = 1; i <= tempdesc.reldum.relatts; ++i)
		if (!tempdesc.relgiven[i])
			tempdesc.relgiven[i] = ++j;
	sortfile(Mod_info.temp_sort, &tempdesc, FALSE);
	if (unlink(Mod_info.outfile) < 0)
		syserr("can't unlink %s", Mod_info.outfile);
	if (link(ztack(REPL_OUT, Fileset), Mod_info.outfile) == -1)
		syserr("can't link %s to %s", ztack(REPL_OUT, Fileset), Mod_info.outfile);
	if (unlink(Mod_info.temp_sort) < 0)
		syserr("sort_isam: can't unlink %s", Mod_info.temp_sort);
	if (unlink(ztack(REPL_OUT, Fileset)) < 0)
		syserr("sort_isam: can't unlink replout file");
}
/*
**	FILL_REL -- Fill the new relation with tuples from either
**		the old relation or the output file of KSORT.
*/

fill_rel(sdesc, desc, sortit)
register DESC	*sdesc, *desc;
char			sortit;
{
	register int	i;
	char		tup_buf[MAXTUP], last_tup[MAXTUP], tup[2 * LIDSIZE]; 
	char		junk[4], newreltype, anytups, chkdups;
	int		need, j, k;
	long		lnum, lid[MAXLID], l, page, t;
	TID		tid, stid, stidlim, ntid, tidpos, btid;
	FILE		*fp, *spfp;
	char		*dp, *sp;
	int		w, temp;
	struct locator	tidloc;

	newreltype = abs(desc->reldum.relspec);
	if (sortit)
	{
		if ((fp = fopen(Mod_info.outfile, "r")) == NULL)
			syserr("FILL_REL: fopen %.14s", Mod_info.outfile);
	}
	else
	{
		cleanrel(sdesc);	/* make sure each page is read fresh */
		find(sdesc, NOKEY, &stid, &stidlim);
	}
	if (newreltype == M_ISAM && (NLidKeys > 0 || !desc->reldum.reldim))
	{
		lnum = 0;
		stuff_page(&tid, &lnum);
		tid.line_id = 0;
		get_page(desc, &tid);
		concat(ISAM_SPOOL, Fileset, Mod_info.spfile);
		/* assume that spool file is not needed */
		spfp = NULL;
		Mod_info.spflag = FALSE;
		if (F_fac == 0)
			F_fac = 100;
		/* setup relgiven field for kcompare later on */
		for (i = 1; i <= desc->reldum.relatts; i++)
			desc->relgiven[i] = desc->relxtra[i];
		if (desc->reldum.reldim)
			Btree_fd = desc->btree_fd;
	}
	desc->reladds = 0;
	for (i = 0; i < desc->reldum.reldim; lid[i++] = 0)
		continue;
	anytups = FALSE;
	chkdups = !sortit && (newreltype != M_ORDER);
# ifdef xZTR2
	if (tTf(36, 3))
	{
		printf("  FILLREL: ");
		printdesc(sdesc);
		printdesc(desc);
	}
# endif
	for (;;)
	{
		w = (newreltype == M_ISAM) ? sdesc->reldum.relwid + desc->reldum.reldim * LIDSIZE : sdesc->reldum.relwid;
		if (sortit)
		{
			i = fread(tup_buf, 1, w, fp);
			if (i == 0)
				break;
			if (i != w)
				syserr("FILL_REL: fread A %d", i);
			if (newreltype == M_HASH && !desc->reldum.reldim)
				if (fread(junk, 1, 4, fp) != 4)
					syserr("FILL_REL: fread B");
		}
		else
		{
#			ifdef xZTR2
			if (tTf(36, 1))
			{
				printf("FILL_REL: stid ");
				dumptid(&stid);
				printf("FILL_REL: stidlim ");
				dumptid(&stidlim);
			}
#			endif
			i = get(sdesc, &stid, &stidlim, tup_buf, TRUE);
#			ifdef xZTR2
			if (tTf(36, 1))
			{
				printf("FILLREL: get %d ", i);
				printup(sdesc, tup_buf);
			}
#			endif
			if (i < 0)
				syserr("FILL_REL: get %d", i);
			if (i == 1)
				break;
		}
		if (newreltype != M_ISAM || (newreltype == M_ISAM && NLidKeys == 0 && desc->reldum.reldim > 0))
		{
			for (j = 0; j < desc->reldum.reldim; ++j)
				if (j < NLidKeys && j < desc->reldum.reldim - 1)
				{
					dp = tup_buf + (sdesc->reloff[LidKey[j]] & I1MASK);
					sp = last_tup + (sdesc->reloff[LidKey[j]] & I1MASK);
					if (!bequal(dp, sp, sdesc->relfrml[LidKey[j]]) || !lid[j])
					{
						++lid[j];
						for (k = j + 1; k < desc->reldum.reldim; ++k)
							lid[k] = 0;
						break;
					}
				}
				else
				{
					if (!lid[0])
					{
						lid[0] = 1;
						if (!(desc->reldum.reldim -1))
							break;
					}
					++lid[desc->reldum.reldim - 1];
					break;
				}
			Btree_fd = desc->btree_fd;
			if (!desc->reldum.reldim || NLidKeys > 0)
			{
				/* assume unordered so btree inserts done
				** separately */
				temp = 0;
				if (desc->reldum.reldim > 0)
				{
					temp = desc->reldum.reldim;
					desc->reldum.reldim = 0;
					desc->reldum.relwid -= temp * LIDSIZE;
				}
				if ((i = insert(desc, &tid, tup_buf, chkdups)) < 0)
					syserr("FILL_REL: insert %d", i);
				if (NLidKeys > 0)
				{
					bmove(&tid, &stid, LIDSIZE);
					desc->reldum.reldim = temp;
					desc->reldum.relwid += temp * LIDSIZE;
					insert_mbtree(desc, Mod_info.btree, lid, &tid, &tidpos);
				}
			}
			if (desc->reldum.reldim && !NLidKeys)
			{
				/* new relation not changed, only lids added */
				page = RT;
				for (j = 0; j < desc->reldum.reldim - 1; ++j)
				{
					if (!lid[j])
						lid[j] = 1;
					if ((t = get_tid(page, lid[j], &tidloc)) < 0)
					{
						insert_btree(Mod_info.btree, page, lid[j], &ntid, &tidpos, j + 2);
						bmove(&ntid, &page, LIDSIZE);
					}
					else
						bmove(&t, &page, LIDSIZE);
				}
				insert_btree(Mod_info.btree, page, lid[abs(desc->reldum.reldim) - 1], &stid, &tidpos, FALSE);
			}
			bmove(tup_buf, last_tup, sdesc->reldum.relwid);
			if (desc->reldum.reldim > 0)
			{
				dp = tup_buf + desc->reldum.relwid - desc->reldum.reldim * LIDSIZE;
				bmove(lid, dp, LIDSIZE * desc->reldum.reldim);
			}
#			ifdef xZTR2
			if (tTf(36, 2))
			{
				printf("FILL_REL: insert ");
				printup(desc, tup_buf);
				printf("FILL_REL: insert ret %d at", i);
				dumptid(&tid);
			}
#			endif
			continue;
		}
		if (anytups)
			i = kcompare(desc, tup_buf, last_tup);
		else
		{
			anytups = TRUE;
			i = 1;
		}
		bmove(tup_buf, last_tup, desc->reldum.relwid);
		need = canonical(desc, tup_buf);
		if (i == 0 && need > space_left(Acc_head))
		{
			/* spool out this tuple. will go on overflow page later */
			if (spfp == NULL)
			{
				if ((spfp = fopen(Mod_info.spfile, "w")) == NULL)
					syserr("FILL_REL: fopen %.14s", Mod_info.spfile);
				Mod_info.spflag = TRUE;
			}
			if (fwrite(tup_buf, 1, desc->reldum.relwid, spfp) != desc->reldum.relwid)
				syserr("FILL_REL: putb spool");
			continue;
		}
		j = (100 - F_fac) * MAXTUP / 100;
		if (j < need)
			j = need;
		if (i != 0 && j > space_left(Acc_head))
		{
			if (i = add_prim(desc, &tid))
				syserr("FILL_REL: force ovflo %d", i);
		}
		tid.line_id = newlino(need);
		put_tuple(&tid, Acctuple, need);
		if (NLidKeys > 0)
		{
			bmove(tup_buf + desc->reldum.relwid - LIDSIZE * desc->reldum.reldim, lid, LIDSIZE * desc->reldum.reldim);
			page = RT;
			for (j = 0; j < desc->reldum.reldim; ++j)
			{
				if ((t = get_tid(page, lid[j], &tidloc)) < 0)
					syserr("get_tid error in modify isam ordered");
				page = t;
			}
			stuff_page(&btid, &tidloc.pageno);
			btid.line_id = tidloc.page.node.leafnode.tid_loc[tidloc.offset];
			/* place proper tid in tree */
			replace_btree(tid, &btid);
		}
		desc->reladds++;
	}
	if (sortit)
	{
		fclose(fp);
		unlink(Mod_info.outfile);
	}
	if (newreltype == M_ISAM && desc->reldum.reldim <= 0)
	{
		if (i = pageflush(Acc_head))
			syserr("fill_rel:pg clean %d", i);
		if (spfp != NULL)
			fclose(spfp);
	}
	if (!desc->reldum.reldim || NLidKeys > 0)
		desc->reldum.reltups = desc->reladds;
	desc->reladds = 0;
	return (0);
}


/*
**	BLDINDEX -	
**
**	Parameters:
**		d - descriptor for relation
**
**	Return Codes:
**		0 - ok
**		<0 - error
**
**	Trace Flags:
**		Z38.7, Z38.8
**
**	Called by:
**		modify
**
*/
bldindex(d)
register DESC	*d;
{
	register TID	*tid;
	register int	tmp;
	TID		tidx;
	struct accbuf	dirbuf;
	int		keywid, level, savespec, keyx[MAXDOM];
	int		offset, len;
	char		tuple[MAXTUP], temptup[MAXTUP], *key;
	long		pageid, start, stop, newstart, newstop;

	tid = &tidx;
	keywid = 0;
	for (tmp = 0; tmp < MAXDOM; tmp++)
		keyx[tmp] = 0;
	for (tmp = 1; tmp <= d->reldum.relatts; tmp++)
		if (d->relxtra[tmp] > 0)
		{
			keyx[d->relxtra[tmp] - 1] = tmp;
			keywid += d->relfrml[tmp] & I1MASK;
		}

	/* Determine the last page of the relation. This will
	** only work if all pages have been written out. Fill_rel
	** must guarantee that all pages have been written
	*/
	level = 0;
	last_page(d, tid, 0);
	pluck_page(tid, &stop);
	start = 0;
	dirbuf.filedesc = d->relfp;
	dirbuf.rel_tupid = d->reltid.ltid;
	savespec = d->reldum.relspec;
	for (;;)
	{
#		ifdef xZTR2
		if (tTf(38, 7))
			printf("isam: level %d\n", level);
#		endif
		dirbuf.ovflopg = start;
		dirbuf.mainpg = level;
		dirbuf.thispage = stop + 1;
		dirbuf.linetab[0] = (short) (dirbuf.firstup - (char *) &dirbuf);
		offset = dirbuf.linetab[0];
		dirbuf.bufstatus = BUF_DIRTY | BUF_DIRECT;

		dirbuf.nxtlino = 0;
		newstart = stop + 1;
		newstop = newstart;
		for (pageid = start; pageid <= stop; pageid++)
		{
#			ifdef xZTR2
			if (tTf(38, 8))
				printf("isam:get key from %ld\n", pageid);
#			endif
			stuff_page(tid, &pageid);
			tid->line_id = 0;
			if (tmp = get(d, tid, tid, tuple, FALSE))
			{
				/*
				** If the relation is empty, then page 0 will
				** return AMINVL_ERR on a get(). Form a blank tuple
				** and use it to create a one tuple directory
				*/
				if (pageid == 0 && tmp == AMINVL_ERR)
				{
					clr_tuple(d, tuple);
				}
				else
				{
					return (-2);
				}
			}

			/*
			** If this is the first level then form the tuple
			** from the mainpage of the relation. Otherwise
			** the tuple is the first tuple of a directory page
			** and it is already correctly formed.
			*/
			if (level == 0)
			{
				key = temptup;
				for (tmp = 0; keyx[tmp] != 0; tmp++)
				{
					len = d->relfrml[keyx[tmp]] & I1MASK;
					bmove(&tuple[d->reloff[keyx[tmp]]], key, len);
					key += len;
				}
				key = temptup;
			}
			else
				key = tuple;

			if (keywid > space_left(&dirbuf))
			{
				if (pageflush(&dirbuf))
					return (-3);
				dirbuf.thispage++;
				newstop = dirbuf.thispage;
				dirbuf.ovflopg = pageid;
				dirbuf.linetab[0] = (short) (dirbuf.firstup - (char *) &dirbuf);
				offset = dirbuf.linetab[0];
				dirbuf.bufstatus = BUF_DIRTY;
				dirbuf.nxtlino = 0;
			}
			/* copy key to directory page */
			bmove(key, (char *) &dirbuf + offset, keywid);

			/* update next line number */
			offset += keywid;
			dirbuf.nxtlino++;
			dirbuf.linetab[-dirbuf.nxtlino] = offset;
		}
		if (pageflush(&dirbuf))
			return (-4);
		if (newstart == newstop)
			break;
		d->reldum.relspec = abs(d->reldum.relspec);
		level++;
		start = newstart;
		stop = newstop;
	}
	d->reldum.relspec = savespec;
	d->reldum.relprim = newstart;
	return (0);
}
/*
**	UNSPOOL -- Take tuples saved in spool file and insert them
**		in new relation.  This is only for ISAM relations.
*/

unspool(sdesc, desc)
register DESC	*sdesc, *desc;
{
	register int	i, j;
	TID		tid, btid;
	char		tup_buf[MAXTUP], tup[2 * LIDSIZE];
	FILE		*spfp;
	long		lid[MAXLID], page, t;
	int		w;
	struct locator  tidpos;

	w = sdesc->reldum.relwid + desc->reldum.reldim * LIDSIZE;
	if (Mod_info.spflag)
	{
		if ((spfp = fopen(Mod_info.spfile, "r")) == NULL)
			syserr("UNSPOOL: fopen spool");
		while ((i = fread(tup_buf, 1, w, spfp)) == w)
		{
			if ((i = insert(desc, &tid, tup_buf, FALSE)) < 0)
				syserr("UNSPOOL: insert %.14s %d", desc->reldum.relid, i);
			if (NLidKeys > 0)
			{
				bmove(tup_buf + desc->reldum.relwid - LIDSIZE * desc->reldum.reldim, lid, LIDSIZE * desc->reldum.reldim);
				page = RT;
				for (j = 0; j < desc->reldum.reldim; ++j)
				{
					if ((t = get_tid(page, lid[j], &tidpos)) < 0)
						syserr("get_tid error in unspool");
					page = t;
				}
				stuff_page(&btid, &tidpos.pageno);
				btid.line_id = tidpos.page.node.leafnode.tid_loc[tidpos.offset];
				replace_btree(tid, &btid);
			}
		}
		if (i != 0)
			syserr("UNSPOOL: read %d", i);
		fclose(spfp);
		unlink(Mod_info.spfile);
	}
	desc->reldum.reltups += desc->reladds;
	desc->reladds = 0;
	return (0);
}
/*
**	FILL_BATCH -- Create and fill a batch file containing the
**		updates for the system catalog so that MODIFY will
**		be recoverable if the system crashes.
*/

fill_batch(odesc, desc)
DESC		*odesc;
register DESC	*desc;
{
	register DESC		*dessys;
	register int		i, k;
	struct relation		reltup, rkey;
	TID			tid, lotid, hitid;
	struct attribute	atttup, akey;
	int			numatts, j;
	char			prebatch[MAXNAME + 4], modbatch[MAXNAME + 4];

	if (bequal(desc->reldum.relid, "relation    ", 12))
	{
		clearkeys(desc);
		setkey(desc, &rkey, desc->reldum.relid, RELID);
		setkey(desc, &rkey, desc->reldum.relowner, RELOWNER);
		if (i = getequal(desc, &rkey, &reltup, &tid))
			syserr("FILL_BATCH: geteq rel rel %d", i);
		bmove(&tid, &desc->reltid, sizeof desc->reltid);
	}
	else
		bmove(&odesc->reltid, &desc->reltid, sizeof desc->reltid);
	resetacc(Acc_head);
	concat(MOD_PREBATCH, Fileset, prebatch);
	close(creat(prebatch, FILEMODE));
	if ((Batch_fp = open(prebatch, O_RDWR)) < 0)
		syserr("FILL_BATCH: open %.14s %d", prebatch, Batch_fp);
	smove(Fileset, Batchbuf.file_id);
	Batch_cnt = 0;
	wrbatch(desc, sizeof *desc);
	if (bequal(desc->reldum.relid, "attribute   ", 12))
		dessys = desc;
	else
		dessys = &Admin.adattd;
	clearkeys(dessys);
	setkey(dessys, &akey, desc->reldum.relid, ATTRELID);
	setkey(dessys, &akey, desc->reldum.relowner, ATTOWNER);
	if (i = find(dessys, EXACTKEY, &lotid, &hitid, &akey))
		syserr("FILL_BATCH: find %d", i);

	/* if ordered relation, one of attributes is LID field */
	numatts = j = desc->reldum.relatts - desc->reldum.reldim;

	while(!(i = get(dessys, &lotid, &hitid, &atttup, TRUE)) && j > 0)
		if (!kcompare(dessys, &akey, &atttup))
			if (atttup.attid <= numatts)
			{
				j--;
				atttup.attxtra = desc->relxtra[atttup.attid];
				wrbatch(&lotid, sizeof lotid);
				wrbatch(&atttup, sizeof atttup);
			}
	for (k = 1; k <= desc->reldum.reldim; ++k)
	/* create new tuple corresponding to LID field; LID field is the
	** last field of relation, a 4-byte integer 
	*/
	{
		smove(desc->reldum.relid, atttup.attrelid);
		bmove(desc->reldum.relowner, atttup.attowner, 2);
		atttup.attid = numatts + k;
		smove(Lid[k - 1], atttup.attname);
		pad(atttup.attname, MAXNAME);
		atttup.attoff = desc->reldum.relwid - (desc->reldum.reldim - k + 1) * LIDSIZE;
		atttup.attfrmt = INT;
		atttup.attfrml = LIDSIZE;
		atttup.attxtra = 0;
		wrbatch(&atttup, sizeof atttup);
	}
	if (i < 0 || j > 0)
		syserr("FILL_BATCH: get att %d count %d", i, j);
	/* get rid of attribute pages */
	cleanrel(dessys);
	flushbatch();
	close(Batch_fp);
	concat(MODBATCH, Fileset, modbatch);
	if (link(prebatch, modbatch) == -1)
		syserr("FILL_BATCH: can't link %.14s %.14s",
			prebatch, modbatch);
	unlink(prebatch);
	return (0);

}

/*
**	MAKE_BSEC -- Creates the seecondary btree relation by first creating
**		a heaped relation.  The main relation tids are found by
**		scanning the leaves of the btree.  The relation is then 
**		modified to an isam relation.
*/	

make_bsec(relname, dim)
char *relname;
int dim;
{
	PARM		pv[8];
	register int	i;
	DESC		bdesc;
	TID		tid, btid;
	long		mtid, page, t, next;
	char		tuple[2 * LIDSIZE], btree[MAXNAME], btreefile[MAXNAME + 4];
	struct locator	tidpos;
	extern char	*iocv();
	extern DESC	Reldes;
        extern int    create();

	pv[0].pv_val.pv_str = "0000002";
	capital(trim_relname(relname), btree);
	pv[1].pv_val.pv_str = btree;
	pv[2].pv_val.pv_str = "mtid";
	pv[3].pv_val.pv_str = "i4";
	pv[4].pv_val.pv_str = "btid";
	pv[5].pv_val.pv_str = "i4";
	pv[6].pv_type = PV_EOF;
	if (create(6, pv))
		syserr("can't create btreesec %s", pv[1].pv_val.pv_str);
	
	if (noclose(&Reldes))
		syserr("noclose in make_bsec");

	if (i = openr(&bdesc, OR_WRITE, btree))
		syserr("opening bsec relation %d", i);
	btreename(relname, btreefile);
	if ((Btree_fd = open(btreefile, O_RDWR)) < 0)
		syserr("make_bsec: can't open %s", btreefile);
	page = RT;
	for (i = 0; i < dim - 1; ++i)
	{
		t = get_tid(page, 1, &tidpos);
		if (t < 0)
			break;	/* lid value doesn't exist */
		bmove(&t, &page, LIDSIZE);
	}
	if (t >= 0)	/* only do inserts if there are lids! */
	{
		do
		{
			get_node(page, &tidpos.page);
			next = tidpos.page.nexttree;
			get_tid(page, 1, &tidpos);
			page = tidpos.pageno;
			for (;;)
			/* scan through leaves of btree */
			{
				stuff_page(&btid, &page);
				for (i = 0; i < tidpos.page.nelmts; ++i)
				{
					btid.line_id = tidpos.page.node.leafnode.tid_loc[i];
					mtid = tidpos.page.node.leafnode.tid_pos[btid.line_id];
					/* form tuple */
					bmove(&mtid, tuple, LIDSIZE);
					bmove(&btid, tuple + LIDSIZE, LIDSIZE);
					if (insert(&bdesc, &tid, tuple, TRUE) < 0)
						syserr("insert error in btreesec");
				}
				page = tidpos.page.node.leafnode.nextleaf;
				if (page == NULL)
					break;
				else 
					get_node(page, &tidpos.page);
			}
		} while (page = next);
	}
	close(Btree_fd);
	closer(&bdesc);

	/* modify to isam on mtid */
	pv[0].pv_val.pv_str = btree;
	pv[1].pv_val.pv_str = "isam";
	pv[2].pv_val.pv_str = "name";
	pv[3].pv_val.pv_str = "mtid";
	pv[4].pv_val.pv_str = "\0";
	pv[5].pv_val.pv_str = "fillfactor";
	/* use fillfactor provided for main relation */
	if (F_fac == 0)
		pv[6].pv_val.pv_str = iocv(80);
	else
		pv[6].pv_val.pv_str = iocv(F_fac);
	pv[7].pv_type = PV_EOF;
	if (modify(7, pv))
		syserr("can't modify btreesec to isam");
}
