P8000/WEGA/contrib/ingres/dbs/src/support/creatdb.c

1384 lines
33 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# include <stdio.h>
# include "gen.h"
# include "../h/dbs.h"
# include "../h/aux.h"
# include "../h/access.h"
# include "../h/pipes.h"
# include "../h/symbol.h"
# include "../h/lock.h"
# include "../h/bs.h"
# ifdef P8000
ret_buf AAjmp_buf;
# else
jmp_buf AAjmp_buf;
# endif
# ifdef ESER
# define getw gethw
# ifdef ESER_PSU
union hword
{
char hw_char[2];
short hw_short;
};
gethw(fp)
FILE *fp;
{
union hword hw;
hw.hw_char[0] = getc(fp);
hw.hw_char[1] = getc(fp);
return (hw.hw_short);
}
# endif
# endif
/*
** CREATDB -- create database (or modify database status)
**
** This program creates a new database. It takes the name of
** the new database (syntax defined below) and a series of
** flags (also defined below).
**
** In order to perform this command, you must be enabled by
** having the U_CREATDB bit set in the user status word
** of the AA_USERS.
**
** The -m flag specifies that the directory for the database
** already exists. It stands for "mounted-file-system",
** because this is presumably when you might use this feature.
** The directory must be empty.
**
** The -e flag specifies that the database already exists.
** It must be in all ways a valid database. This mode allows
** you to turn flags on and off, as controlled by the other
** flags.
**
** Usage:
** creatdb [flags] databasename
**
** Positional Parameters:
** databasename -- the name of the database to create.
** It must conform to all the usual rules
** about names. Notice that this is more
** restrictive than BS usually is: names
** must begin with an alpha, and must be
** composed of alphanumerics. It may be
** at most MAXNAME characters long. Underscore
** counts as an alphabetic.
**
** Flags:
** -m
** This is a mounted filesystem. Actually,
** this just means that the directory in which
** the database is to reside already exists.
** It must be empty.
** -e
** This database exists. When the -e flag is
** specified, the database is brought up to date,
** rather than created. Things which may be
** changed with the -e flag is anything that
** affects the database status or the relation
** status bits.
** -uXX
** Run as user XX (usercode or login name). This
** flag may only be used by the DBS superuser.
** Normally, the database administrator for the
** new database is the user who performs the
** command, but the -u flag allows DBS to
** give the database to someone else. Thus, it
** is possible for someone to be a DBA without
** having the U_CREATDB bit set.
** -Fpathname
** Use the named file as the database template.
** This is, of course, for debugging use only.
** +-c
** Turn concurrency control on/off. The default
** for a new database depends on the DBTMPLTFILE,
** but as of this writing it defaults on.
** +-q
** Turn query modification on/off.
** +-l
** Turn protection violation logging on/off.
**
** Files:
** USE_CREATDB -- to print a "usage: ..." message.
** DBTMPLTFILE<VERSION>
** This file drives the entire program. The
** syntax of this file is defined below in
** readdbtemp(). Briefly, it tells the database
** status, the relations in an 'empty' database,
** and the status and format of those relations.
** .../data/base
** This directory is the directory in which all
** databases eventually reside. Each database is
** a subdirectory of this directory.
**
** Return Codes:
** zero -- success
** else -- failure.
**
** Defined Constants:
** MAXRELNS
** This defines the maximum number of relations
** which may be declared in the DBTMPLTFILE.
** MAXDBTEMP
** The maximum size of the DBTMPLTFILE. This
** determines the maximum number of characters
** which may be in the file.
**
** Requires:
** Much. Notably, be careful about changes to any
** access methods, especially automatic references to
** to the AA_ADMIN file or AA_REL relation.
*/
# define MAXRELNS 20
# define MAXDBTEMP 2000
/* AA_REL & AA_ATT reln descriptors used in access methods */
extern DESC Reldes;
extern DESC Attdes;
extern int AAdbstat; /* user status, set by initucode */
extern char *AAdbpath;
extern char *AAztack();
int Dbstat; /* database status */
# ifdef VPGSIZE
int AApgsize = PGSIZE;
int AAmaxtup = MAXTUP;
int AAltsize = 0;
# endif
int Dbson;
int Dbsoff; /* same: bits turned on, bits turned off */
char Delim;
char DBTMPLTFILE[] = "/sys/etc/dbtmplt";
char MKDIR[] = "mkdir";
char SYSNAME[] = "_SYS";
int s_SYSNAME = sizeof SYSNAME - 1;
char USE_CREATDB[] = "/sys/etc/creatdb_use";
struct reldes
{
int bitson;
int bitsoff;
char *parmv[MAXPARMS];
};
struct reldes Rellist[MAXRELNS];
main(argc, argv)
int argc;
char *argv[];
{
register int i;
register char *p;
register short *flagv;
register char *dbtmpfile;
register char *database;
register int faterr;
register int exists;
char **av;
int admin;
int code;
struct stat stbuf;
char adminbuf[BUFSIZ];
extern int errno;
extern struct admin AAdmin;
extern char AAversion[];
extern char *AAproc_name;
extern char *Parmvect[];
extern char *Flagvect[];
extern short *flaglkup();
# ifdef NON_V7_FS
extern char *read_dir();
register char *direc;
# endif
AAproc_name = "CREATDB";
# ifdef xSTR1
AAtTrace(&argc, argv, 'X');
# endif
/*
** The concurrency device will be opened for writing.
** If the lock device isn't installed, then AAlockdes = -1
** and no locking will (or can) occure.
*/
AAlockdes = open(AA_DBS_LOCK, 1);
errno = 0; /* clear in case AA_DBS_LOCK isn't available */
AALOCKREQ.l_db = 0L;
AALOCKREQ.l_pid = getpid();
if (AAsetcsl(NO_REL) < 0)
AAsyserr(0, "Zeitweilig koennen keine Datenbanken erzeugt werden");
exists = 0;
i = initucode(argc, argv, TRUE, (char *) 0, -1);
database = Parmvect[0];
switch (i)
{
case 0:
case 5:
exists = 1;
break;
case 6:
exists = -1;
case 1:
break;
case 2:
AAsyserr(0, "Sie duerfen auf die Datenbank '%s' nicht zugreifen", database);
case 3:
AAsyserr(0, "Sie sind kein eingetragener DBS-Nutzer");
case 4:
printf("Sie haben keinen Datenbanknamen angegeben\n");
usage:
/* cat USE_CREATDB */
AAcat(AAztack(AApath, USE_CREATDB));
term:
AAunlall();
fflush(stdout);
exit(-1);
default:
AAsyserr(22200, AAproc_name, i);
}
faterr = 0;
dbtmpfile = (char *) 0;
for (av = Flagvect; p = *av; av++)
{
if (p[0] != '-' && p[0] != '+')
AAsyserr(21010, p);
switch (p[1])
{
case 'F': /* DBTMPLTFILE */
if (!p[2])
goto badflag;
dbtmpfile = &p[2];
break;
# ifdef VPGSIZE
case '1': /* BLOCKSIZE */
case '2':
case '4':
case '8':
i = p[1] - '0';
if (p[2] || i > PGBLKS)
goto badflag;
AApgsize = BLKSIZ * i;
AAltsize = (PGSIZE - AApgsize) / sizeof (short);
break;
# endif
case 'C':
p[1] = 'c';
goto def;
case 'E':
p[1] = 'e';
goto def;
case 'M':
p[1] = 'm';
goto def;
case 'Q':
p[1] = 'q';
/* fall through */
default:
def:
if (flagv = flaglkup(p[1], p[0]))
{
*flagv = ((p[0] == '+')? 1: -1);
continue;
}
badflag:
printf("Unbekanntes Flag '%s'\n", p);
faterr++;
continue;
}
if (p[0] == '+')
goto badflag;
}
/* check for legality of database name */
if (Parmvect[1])
{
printf("Zu viele Argumente\n");
goto usage;
}
if (!check(database))
AAsyserr(0, "Unerlaubter Datenbankname '%s'", database);
if (!(AAdbstat & U_CREATDB))
AAsyserr(0, "Sie duerfen keine Datenbanken erzeugen");
/* end of input checking */
if (faterr)
goto term;
/* now see if it should have been there */
if (flagval('m') || flagval('e'))
{
# ifdef xSTR3
AAtTfp(1, 14, "AAdbpath = '%s'\n", AAdbpath);
# endif
if (flagval('e') && exists <= 0)
AAsyserr(0, "Die Datenbank '%s' existiert nicht", database);
if (chdir(AAdbpath) < 0)
AAsyserr(0, "Es existiert kein directory fuer die Datenbank '%s'", database);
if (!flagval('e'))
{
/* make certain that it is empty */
# ifdef NON_V7_FS
open_dir(".");
while (direc = read_dir())
{
/* throw out legitimate files */
if (AAsequal(direc, ".") || AAsequal(direc, ".."))
continue;
AAsyserr(0, "directory '%s' ist nicht leer", database);
}
close_dir();
# else
freopen(".", "r", stdin);
for (i = 0; i < 16; i++)
getw(stdin);
while ((i = getw(stdin)) != EOF)
{
if (i)
AAsyserr(0, "directory '%s' ist nicht leer", database);
for (i = 0; i < 7; i++)
getw(stdin);
}
# endif
}
else
{
/* check for correct owner */
AAam_start();
if (!AAbequal(AAusercode, AAdmin.adhdr.adowner, 2))
AAsyserr(0, "Sie sind nicht der DBA der Datenbank '%s'", database);
}
}
else
{
if (exists)
AAsyserr(0, "Die Datenbank '%s' existiert bereits", database);
/* create it */
fflush(stdout);
if ((i = fork()) < 0)
AAsyserr(21013);
if (!i)
{
/* enter database directory */
initdbpath((char *) 0, adminbuf, FALSE);
if (chdir(adminbuf) < 0)
AAsyserr(22201, AAproc_name, adminbuf);
/* arrange for proper permissions */
if (setuid(getuid()))
AAsyserr(21015);
if (setgid(getgid()))
AAsyserr(21016);
fflush(stdout);
execlp(MKDIR, MKDIR, database, 0);
AAsyserr(21017, MKDIR);
}
while (wait(&code) != -1)
continue;
if (chdir(AAdbpath) < 0)
AAsyserr(22201, AAproc_name, AAdbpath);
fflush(stdout);
if ((i = fork()) < 0)
AAsyserr(21019);
if (!i)
{
setuid(getuid());
if (chmod(".", 0777))
AAsyserr(21020);
exit(0);
}
while (wait(&code) != -1)
continue;
if (ctou(code))
{
fflush(stdout);
AAunlall();
exit(code);
}
}
/* reset 'errno', set from possibly bad chdir */
errno = 0;
/* Set up locking information. */
AALOCKREQ.l_db = 0L;
AAunlcs(NO_REL);
stat(".", &stbuf);
AAbmove(&stbuf, &AALOCKREQ.l_db, sizeof (long));
if (AAsetdbl(A_RTN, M_EXCL) < 0)
AAsyserr(0, "Datenbank zeitweilig nicht verfuegbar");
/* determine name of DBTMPLTFILE and open */
if (!dbtmpfile)
{
AAsmove(AAversion, adminbuf);
for (i = 0; adminbuf[i]; i++)
if (adminbuf[i] == '/')
break;
adminbuf[i] = 0;
dbtmpfile = AAztack(AAztack(AApath, DBTMPLTFILE), adminbuf);
}
if (!freopen(dbtmpfile, "r", stdin))
AAsyserr(21021);
readdbtemp();
/* check for type -- update database status versus create */
if (flagval('e'))
changedb();
else
makedb();
/* close the cache descriptors */
# ifdef xSTR3
if (AAtTfp(50, 0, "Attdes.reltid = "))
{
AAdumptid(&Attdes.reltid);
printf("Reldes.reltid = ");
AAdumptid(&Reldes.reltid);
}
# endif
if (i = AArelclose(&Attdes))
AAsyserr(21022, i);
if (i = AArelclose(&Reldes))
AAsyserr(21023, i);
/* bring tupcount in AA_ADMIN file to date */
AAbmove(&Reldes.reldum.reltups, &AAdmin.adreld.reldum.reltups,
sizeof Reldes.reldum.reltups);
AAbmove(&Attdes.reldum.reltups, &AAdmin.adattd.reldum.reltups, sizeof Attdes.reldum.reltups);
/* clean up some of the fields to catch problems later */
AAdmin.adreld.relfp = AAdmin.adattd.relfp = -1;
AAdmin.adreld.relopn = AAdmin.adattd.relopn = 0;
if ((admin = creat(AA_ADMIN, FILEMODE)) < 0)
AAsyserr(21024);
if (write(admin, &AAdmin, sizeof AAdmin) != sizeof AAdmin)
AAsyserr(21025);
close(admin);
/* exit successfully */
AAunldb();
fflush(stdout);
exit(0);
}
/*
** Rubout processing.
*/
rubproc()
{
AAunlall();
fflush(stdout);
exit(-2);
}
/*
** READDBTEMP -- read the DBTMPLTFILE and build internal form
**
** This routine reads the DBTMPLTFILE (presumably openned as
** the standard input) and initializes the Dbstat (global database
** status word) and Rellist variables.
**
** Rellist is an array of argument vectors, exactly as passed to
** 'create'.
**
** The syntax of the DBTMPLTFILE is as follows:
**
** The first line is a single status word (syntax defined below)
** which is the database status.
**
** The rest of the file is sets of lines separated by blank lines.
** Each set of lines define one relation. Two blank lines in a
** row or an end-of-file define the end of the file. Each set
** of lines is broken down:
**
** The first line is in the following format:
** relname:status
** which defines the relation name and the relation status for
** this relation (syntax defined in 'getstat' below). Status
** may be omitted, in which case a default status is assumed.
**
** Second through n'th lines of each set define the attributes.
** They are of the form:
** attname format
** separated by a single tab. 'Format' is the same as on a
** 'create' statement in QL.
**
** Notice that we force the S_CATALOG bit to be on in any
** case. This is because the changedb() routine will fail
** if the -e flag is ever used on this database if any
** relation appears to be a user relation.
**
** Side Effects:
** Dbstat gets the database status.
** Rellist is created with a list of the relations,
** (as parameter vectors -- just as passed to
** create). The entry after the last entry
** has its pv[0] == (char *) 0.
*/
readdbtemp()
{
static char buf[MAXDBTEMP];
register struct reldes *r;
register char **q;
register int i;
register char *p;
register int j;
register int defrstat;
int bitson;
int bitsoff;
/* read database status */
defrstat = S_CATALOG | S_NOUPDT /* | S_CONCUR */ | S_PROTALL;
bitson = bitsoff = 0;
Dbstat = getstat(A_DBCONCUR, &Dbson, &Dbsoff);
if (Delim == ':')
defrstat = getstat(defrstat, &bitson, &bitsoff);
if (Delim != '\n')
AAsyserr(21026, Delim);
/* compute default relation status */
/* start reading relation info */
p = buf;
for (r = Rellist; ; r++)
{
r->bitson = bitson;
r->bitsoff = bitsoff;
q = r->parmv;
/* get relation name */
q[1] = p;
p += getname(p) + 1;
/* test for end of DBTMPLTFILE */
if (!q[1][0])
break;
/* get relation status */
i = getstat(defrstat, &r->bitson, &r->bitsoff);
i |= S_CATALOG; /* guarantee system catalog */
*q++ = p;
*p++ = ((i >> 15) & 1) + '0';
for (j = 12; j >= 0; j -= 3)
*p++ = ((i >> j) & 07) + '0';
*p++ = 0;
q++;
if (Delim != '\n')
AAsyserr(21027, Delim);
/* read attribute names and formats */
for ( ; ; )
{
/* get attribute name */
*q++ = p;
p += getname(p) + 1;
if (!q[-1][0])
break;
if (Delim != '\t')
AAsyserr(21028, Delim);
/* get attribute type */
*q++ = p;
p += getname(p) + 1;
if (Delim != '\n')
AAsyserr(21029, Delim);
}
/* insert end of argv signal */
*--q = (char *) 0;
/* ad-hoc overflow test */
if (p >= &buf[MAXDBTEMP])
AAsyserr(21030);
}
/* mark the end of list */
q[1] = (char *) 0;
}
/*
** GETSTAT -- Get status word
**
** A status word is read from the standard input (presumably
** DBTMPLTFILE). The string read is interpreted as an octal
** number. The syntax is:
** N{+c+N[~N]}
** where N is a number, + is a plus or a minus sign, and c is
** a flag. '+c+N1[~N2]' groups are interpreted as follows:
** If flag 'c' is set (assuming the preceeding character is a +,
** clear if it is a -), then set (clear) bits N1. If tilde N2
** is present, then if flag 'c' is unset (called as '-c' ('+c'))
** clear (set) bits N2; if ~N2 is not present, clear (set)
** bits N1.
**
** For example, an entry might be (but probably isn't):
** 1-c-1+q+6~2
** having the following meaning:
**
** 1. Default to the 1 bit set.
**
** 2. If the -c flag is specified, clear the '1' bit. If the
** +c flag is specified, set the '1' bit. If it is unspecified,
** leave the '1' bit alone.
**
** 3. If the +q flag is specified, set the '2' bit and the '4'
** bit. If the -q flag is specified, clear the '2' bit (but leave
** the '4' bit alone). If the +-q flag is unspecified, leave
** those bits alone.
**
** Thus, a database with this entry is initially created with
** the 1 bit on. The '4' bit is a history, which says if the
** 'q' flag has ever been set, while the '2' bit tells if it is
** currently set.
**
** Parameters:
** def -- the default to return if there is no number
** there at all.
** bitson -- a pointer to a word to contain all the
** bits to be turned on -- used for the -e flag.
** bitsoff -- same, for bits turned off.
**
** Returns:
** The value of the status word.
** There are no error returns.
*/
getstat(def, bitson, bitsoff)
int def;
int *bitson;
int *bitsoff;
{
register int c;
register int st;
register int i;
register int setbits;
register int clrbits;
register int ctlch;
/* reset bits being turned on and off */
*bitson = *bitsoff = 0;
/* check to see if a base status word is defined */
if (Delim == '\n' || (Delim = c = getchar()) < '0' || c > '7')
st = def; /* no, use default */
else
{
/* read base status field */
ungetc(c, stdin);
st = roctal();
}
/* scan '+c+N' entries */
for ( ; ; )
{
/* check for a flag present */
c = Delim;
if (c == '\n' || c == ':')
return (st);
if (c != '+' && c != '-')
{
badfmt:
AAsyserr(21031, c);
}
/* we have some flag -- get it's value */
i = flagval(getchar());
/* save sign char on flag */
ctlch = c;
/* get sign on associated number and the number */
if ((c = getchar()) != '+' && c != '-')
goto badfmt;
setbits = roctal();
/* test whether action on -X same as on +X */
if (Delim == '~')
clrbits = roctal(); /* they have different bits (see above) */
else
clrbits = setbits; /* on 'creatdb -e -X', use opposite bits of +X */
/* test for any effect at all */
if (!i)
continue;
/* test whether we should process the '+N' */
if ((ctlch == '-')? (i < 0): (i > 0))
i = setbits;
else
{
i = clrbits;
/* switch sense of bit action */
c = (c == '+')? '-': '+';
}
if (c == '+')
{
st |= i;
*bitson |= i;
}
else
{
st &= ~i;
*bitsoff |= i;
}
}
}
/*
** ROCTAL -- Read an octal number from standard input.
**
** This routine just reads a single octal number from the standard
** input and returns its value. It will only read up to a non-
** octal digit. It will also skip initial and trailing blanks.
** 'Delim' is set to the next character in the input stream.
**
** Returns:
** value of octal number in the input stream.
**
** Side Effects:
** 'Delim' is set to the delimiter which terminated the
** number.
*/
roctal()
{
register int c;
register int val;
val = 0;
/* skip initial blanks */
while ((c = getchar()) == ' ')
continue;
/* get numeric value */
while (c >= '0' && c <= '7')
{
val = (val << 3) | (c - '0');
c = getchar();
}
/* skip trailing blanks */
while (c == ' ')
c = getchar();
/* set Delim and return numeric value */
Delim = c;
return (val);
}
/*
** GETNAME -- get name from standard input
**
** This function reads a name from the standard input. A
** name is defined as a string of letters and digits.
**
** The character which caused the scan to terminate is stored
** into 'Delim'.
**
** Parameters:
** ptr -- a pointer to the buffer in which to dump the
** name.
**
** Returns:
** The length of the string.
*/
getname(p)
register char *p;
{
register int len;
register int c;
for (len = 0; (c = getchar()) != EOF; len++)
{
/* check for end of name */
if ((c < '0' || c > '9') && (c < 'a' || c > 'z'))
break;
/* store character into buffer */
*p++ = c;
}
/* null-terminate the string */
*p = '\0';
/* store the delimiting character and return length of string */
Delim = c;
return (len);
}
/*
** MAKEDB -- make a database from scratch
**
** This is the code to make a database if the -e flag is off.
**
** The first step is to make a copy of the AA_ADMIN file
** in the internal 'AAdmin' struct. This is the code which
** subsequently gets used by AArelopen and opencatalog. Notice
** that the AA_ADMIN file is not written out; this happens after
** makedb returns.
**
** Next, the physical files are created with one initial (empty)
** page. This has to happen before the 'create' call so
** that it will be possible to flush AA_REL and AA_ATT
** relation pages during the creates of the AA_REL and
** AA_ATT relations. Other relations don't need this,
** but it is more convenient to be symmetric.
**
** The next step is to create the relations. Of course, all
** this really is is inserting stuff into the system catalogs.
**
** When we are all done we open the AA_REL relation for the
** admin cache (which of course should exist now). Thus,
** the AArelclose's in main (which must be around to update the
** tuple count) will work right.
**
** Side Effects:
** A database is created!!
** Several files will be created in the current directory,
** one for each relation mentioned in the
** DBTMPLTFILE.
** The 'AAdmin' struct will be filled in.
*/
makedb()
{
register struct reldes *r;
# ifdef VPGSIZE
register int i;
# endif
# ifdef xSTR3
AAtTfp(51, 0, "MAKEDB: AAusercode = %s (0%o)\n", AAusercode, AAusercode);
# endif
/* create the physical files */
for (r = Rellist; r->parmv[1]; r++)
makefile(r);
/* initialize the AA_ADMIN file internal cache */
AAbmove(AAusercode, AAdmin.adhdr.adowner, 2);
# ifdef VPGSIZE
i = AApgsize / BLKSIZ - 1;
Dbstat |= i << 8;
# endif
AAdmin.adhdr.adflags = Dbstat;
makeadmin(&AAdmin.adreld, Rellist[0].parmv);
makeadmin(&AAdmin.adattd, Rellist[1].parmv);
/* done with admin initialization */
/* initialize relations */
for (r = Rellist; r->parmv[1]; r++)
makereln(r);
}
/*
** MAKEADMIN -- manually initialize descriptor for AA_ADMIN file
**
** The relation descriptor pointed to by 'pv' is turned into
** a descriptor, returned in 'd'. Presumably, this descriptor
** is later written out to the AA_ADMIN file.
**
** Notice that the 'reltid' field is filled in sequentially.
** This means that the relations put into the AA_ADMIN file
** must be created in the same order that they are 'made'
** (by this routine), that the format of tid's must not
** change, and that there can not be over one page worth of
** relations in the AA_ADMIN file. Our current system currently
** handles this easily.
**
** Parameters:
** d -- the descriptor to get the result.
** pv -- a parm vector in 'create' format, which drives
** this routine.
*/
makeadmin(des, p)
register DESC *des;
register char **p;
{
static int tid;
register int i;
register int j;
short len;
char fname[MAXNAME + 3];
# ifdef xSTR2
AAtTfp(10, -1, "creating %s in admin\n", p[1]);
# endif
i = AA_oatoi(*p++);
AAdbname(*p++, AAusercode, fname);
AAbmove(fname, des->reldum.relid, MAXNAME + 2);
des->reldum.relstat = i;
des->reldum.relatts = 0;
des->reldum.relwid = 0;
des->reldum.relspec = M_HEAP;
des->reltid = tid++;
if ((des->relfp = open(fname, 2)) < 0)
AAsyserr(21032, fname);
des->relopn = RELWOPEN(des->relfp);
/* initialize domain info */
for ( ; *p++; p++)
{
if ((i = typeconv(p[0][0])) < 0)
AAsyserr(21033, p[0][0]);
j = ++(des->reldum.relatts);
des->relfrmt[j] = i;
if (AA_atoi(&p[0][1], &len))
AAsyserr(21034, p[0]);
des->relfrml[j] = len;
des->reloff[j] = des->reldum.relwid;
des->reldum.relwid += len;
}
}
/*
** MAKEFILE -- make an 'empty' file for a relation
**
** This routine creates a file with a single (empty) page
** on it -- it is part of the 'create' code, essentially.
**
** Parameters:
** rr -- a pointer to the 'reldes' structure for this
** relation (file).
**
** Side Effects:
** A file with one page is created.
*/
makefile(r)
register struct reldes *r;
{
DESC d;
long npages;
AAdbname(r->parmv[1], AAusercode, d.reldum.relid);
# ifdef xSTR1
AAtTfp(12, 0, "creat %s\n", d.reldum.relid);
# endif
if ((d.relfp = creat(d.reldum.relid, FILEMODE)) < 0)
AAsyserr(21035, d.reldum.relid);
npages = 1;
if (formatpg(&d, npages))
AAsyserr(21036);
close(d.relfp);
}
/*
** MAKERELN -- make a relation
**
** This is the second half of the create, started by 'makefile'.
**
** This routine just sets up argument vectors and calls create,
** which does the real work.
**
** Parameters:
** rr -- a pointer to the Rellist entry for the relation
** to be created.
**
** Side Effects:
** Information will be inserted into the AA_REL and
** AA_ATT relations.
*/
makereln(r)
register struct reldes *r;
{
register int pc;
register char **pv;
register int i;
for (pc = 0, pv = r->parmv; *pv; pv++)
pc++;
pv = r->parmv;
# ifdef xSTR1
if (AAtTf(1, 0))
AAprargs(pc, pv);
# endif
if (i = create(pc, pv))
AAsyserr(21037, i);
}
/*
** CHECK -- check database name syntax
**
** The name of a database is checked for validity. A valid
** database name is not more than 14 characters long, begins
** with an alphabetic character, and contains only alpha-
** numerics. Underscore is considered numeric.
**
** Parameters:
** st -- the string to check.
**
** Returns:
** TRUE -- ok.
** FALSE -- failure.
*/
check(p)
register char *p;
{
register int c;
/* check string length */
if (AAlength(p) > MAXNAME)
return (FALSE);
/* check the first character of the string for alphabetic */
if (!(((c = *p++) >= 'a'
# ifdef EBCDIC
&& c <= 'i') || (c >= 'j'
&& c <= 'r') || (c >= 's'
# endif
&& c <= 'z')))
return (FALSE);
/* check the rest for alphanumeric */
while (c = *p++)
{
if (c == '_' || (c >= '0' && c <= '9') || (c >= 'a'
# ifdef EBCDIC
&& c <= 'i') || (c >= 'j'
&& c <= 'r') || (c >= 's'
# endif
&& c <= 'z'))
continue;
return (FALSE);
}
return (TRUE);
}
/*
** FLAGLKUP -- look up user flag
**
** This routine helps support a variety of user flags. The
** routine takes a given user flag and looks it up (via a
** very crude linear search) in the 'Flags' vector, and
** returns a pointer to the value.
**
** The 'flag' struct defines the flags. The 'flagname' field
** is the character which is the flag id, for example, 'c'
** in the flag '-c'. The 'flagtype' field defines how the
** flag may appear; if negative, only '-c' may appear, if
** positive, only '+c' may appear; if zero, either form may
** appear. Finally, the 'flagval' field is the value of the
** flag -- it may default any way the user wishes.
**
** Parameters:
** flagname -- the name (as defined above) of the
** flag to be looked up.
** plusminus -- a character, '+' means the '+x' form
** was issued, '-' means the '-x' form was
** issued, something else means *don't care*.
** If an illegal form was issued (that is,
** that does not match the 'flagtype' field
** in the structure), the "not found" return
** is taken.
**
** Returns:
** (int *) 0 -- flag not found, or was incorrect type,
** as when the '+x' form is specified in the
** parameters, but the 'Flags' struct says
** that only a '-x' form may appear.
** else -- pointer to the 'flagval' field of the correct
** field in the 'Flags' vector.
*/
struct flag
{
char flagname; /* the name of the flag */
char flagtype; /* -1: -x form; +1: +x form; 0: both */
short flagval; /* user-defined value of the flag */
};
struct flag Flags[] =
{
'q', 0, 0,
'l', 0, 0,
'c', 0, 0,
'e', -1, 0,
'm', -1, 0,
0
};
short *flaglkup(f, pm)
register int f;
register int pm;
{
register struct flag *p;
/* look up flag in vector */
for (p = Flags; p->flagname != f; p++)
if (!p->flagname)
return ((short *) 0);
/* found in list -- check type */
if ((pm == '+' && ctoi(p->flagtype) < 0) || (pm == '-' && ctoi(p->flagtype) > 0))
return ((short *) 0);
/* type is OK -- return pointer to value */
return (&p->flagval);
}
/*
** FLAGVAL -- return value of a flag
**
** Similar to 'flaglkup', except that the value is returned
** instead of the address, and no error return can occur.
**
** Parameters:
** f -- the flag to look up (see flaglkup).
**
** Returns:
** The value of flag 'f'.
*/
flagval(f)
register int f;
{
register short *p;
/* get value of flag */
p = flaglkup(f, 0);
/* test for error return, AAsyserr if so */
if (!p)
AAsyserr(21038, f);
/* return value */
return (*p);
}
/*
** CHANGEDB -- change status bits for database/relations
**
** In this function we change the status bits for use with the
** -e flag.
**
** This module always uses the differential status
** change information, so that existing bits are not touched.
**
** We check to see that invalid updates, such as turning off
** query modification when it is already on, can not occur.
** This is because of potential AAsyserr's when the system is
** later run, e.g., because of views without instantiations.
**
** In the second step, the database status is updated. This is
** done strictly in-core, and will be updated in the database
** after we return.
**
** The list of valid relations are then scanned. For each
** relation listed, a series of steps occurs:
**
** (1) The relation is checked for existance. If it does not
** exist, it is created, and we return to the beginning of the
** loop. Notice that we don't have to change modes in this
** case, since it already has been done.
**
** (2) If the relation does exist, we check to see that it
** is a system catalog. If it is not, we have an error, since
** this is a user relation which just happenned to have the
** same name. We inform the user and give up.
**
** (3) If the relation exists, is a system catalog, and all
** that, then we check the changes we need to make in the
** bits. If no change need be made, we continue the loop;
** otherwise, we change the bits and replace the tuple in
** the AA_REL relation.
**
** (4) If the relation being updated was the AA_REL or
** AA_ATT relation, we change the AAdmin struct accordingly.
**
** Notice that the result of all this is that all relations
** which might ever be used exist and have the correct status.
**
** Notice that it is fatal for either the AA_ATT or AA_REL
** relations to not exist, since the file is created at the
** same time that relation descriptors are filled in. This
** should not be a problem, since this is only called on an
** existing database.
**
** As a final note, we open the AA_ATT relation cache not
** because we use it, but because we want to do a AArelclose
** in main() to insure that the tupcount is updated in all
** cases.
**
** Side Effects:
** The database is brought up to date, as described
** above.
** Tuples may be added or changed in system catalogs.
** Files may be created.
*/
changedb()
{
register struct reldes *r;
register int i;
struct tup_id tid;
struct relation relk;
struct relation relt;
# ifdef xSTR1
AAtTfp(40, 0, "CHANGEDB: Dbson=0%o, Dbsoff=0%o\n", Dbson, Dbsoff);
# endif
/* check to see we aren't doing anything illegal */
if (flagval('q') < 0)
AAsyserr(0, " Flag '-q' ist nicht erlaubt");
/* update the database status field */
AAdmin.adhdr.adflags = (AAdmin.adhdr.adflags | Dbson) & ~Dbsoff;
/* open the system catalog caches */
opencatalog(AA_REL, 2);
opencatalog(AA_ATT, 0);
/* scan the relation list:- Rellist */
for (r = Rellist; r->parmv[1]; r++)
{
/* see if this relation exists */
AAam_clearkeys(&Reldes);
AAam_setkey(&Reldes, &relk, r->parmv[1], RELID);
if ((i = AAgetequal(&Reldes, &relk, &relt, &tid)) < 0)
AAsyserr(21040);
if (i > 0)
{
/* doesn't exist, create it */
printf("Erzeuge Katalog '%s'\n", r->parmv[1]);
makefile(r);
makereln(r);
}
else
{
/* exists -- check to make sure it is the right one */
if (!(relt.relstat & S_CATALOG))
{
/* exists as a user reln -- tough luck buster */
AAsyserr(0, "Katalog '%s' existiert bereits als Nutzerrelation", r->parmv[1]);
}
/* it exists and is the right one -- update status */
if (!r->bitson && !r->bitsoff)
continue;
/* actual work need be done */
relt.relstat = (relt.relstat | r->bitson) & ~r->bitsoff;
/* replace tuple in AA_REL relation */
if (i = AAam_replace(&Reldes, &tid, &relt, FALSE))
AAsyserr(21041, i);
/* update AAdmin struct if AA_REL or AA_ATT */
if (AAsequal(r->parmv[1], AA_REL))
AAdmin.adreld.reldum.relstat = relt.relstat;
else if (AAsequal(r->parmv[1], AA_ATT))
AAdmin.adattd.reldum.relstat = relt.relstat;
}
}
}
/*
** READADMIN -- read the AA_ADMIN file into the 'AAdmin' cache
**
** This routine opens and reads the 'AAdmin' cache from the
** AA_ADMIN file in the current directory.
**
** This version of the routine is modified for creatdb --
** the '-e' flag is checked, and nothing is performed
** unless it is set.
**
** If not set, the AA_REL and AA_ATT relations
** are opened, and the descriptors for them in the AAdmin
** struct are filled in with their file descriptors.
**
** Side Effects:
** The 'AAdmin' struct is filled in.
** The AA_REL and AA_ATT files are
** opened.
*/
AAreadadmin()
{
register int i;
char relname[MAXNAME + 4];
/* read the stuff from the AA_ADMIN file */
if (flagval('e'))
{
if ((i = open(AA_ADMIN, 0)) < 0)
AAsyserr(21042, i);
if (read(i, &AAdmin, sizeof AAdmin) != sizeof AAdmin)
AAsyserr(21043);
close(i);
/* open the physical files for AA_REL and AA_ATT */
AAdbname(AAdmin.adreld.reldum.relid, AAdmin.adreld.reldum.relowner, relname);
if ((AAdmin.adreld.relfp = open(relname, 2)) < 0)
AAsyserr(21044, AAdmin.adreld.relfp);
AAdbname(AAdmin.adattd.reldum.relid, AAdmin.adattd.reldum.relowner, relname);
if ((AAdmin.adattd.relfp = open(relname, 2)) < 0)
AAsyserr(21045, AAdmin.adattd.relfp);
AAdmin.adreld.relopn = RELWOPEN(AAdmin.adreld.relfp);
AAdmin.adattd.relopn = RELROPEN(AAdmin.adattd.relfp);
/* get the page size of data base */
if ((i = (AAdmin.adhdr.adflags >> 8) + 1) > PGBLKS)
AAsyserr(10010, i, PGBLKS);
# ifdef VPGSIZE
AApgsize = BLKSIZ * i;
/* get the linetab size, the difference */
AAltsize = (PGSIZE - AApgsize) / sizeof (short);
# endif
}
return (0);
}