|
|
BSD 4.3
# include <stdio.h>
# include <ingres.h>
# include <aux.h>
# include <version.h>
# include <access.h>
# include <symbol.h>
# include <opsys.h>
# include <pv.h>
# include <sccs.h>
# include <sys/dir.h>
SCCSID(@(#)creatdb.c 8.6 1/31/86)
/*
** 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 users file.
**
** 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 UNIX usually is: names
** must begin with an alpha, and must be
** composed of alphanumerics. It may be
** at most 14 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 INGRES superuser.
** Normally, the database administrator for the
** new database is the user who performs the
** command, but the -u flag allows INGRES 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 dbtmplt file,
** but as of this writing it defaults on.
** +-q
** Turn query modification on/off.
** +-l
** Turn protection violation logging on/off.
**
** Files:
** .../files/dbtmplt<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 dbtmplt file.
** MAXDBTEMP
** The maximum size of the dbtmplt file. This
** determines the maximum number of characters
** which may be in the file.
**
** Compilation Flags:
** xB_UNIX -- if defined, says that we have a "Berkeley
** UNIX" system, with no group id's. If
** undefined, specifies either a version seven
** UNIX (with 16-bit group id's) or version six
** UNIX (with 8-bit group id's); either way,
** "setgid(getgid())" will work.
**
** Trace Flags:
** -Tn, as below:
**
** 1 -- makereln()
** 2 -- create()
** 10 -- makeadmin()
** 12 -- makefile()
** 20 -- makedb()
**
** Compilation Instructions:
** % setup creatdb
**
** - which translates to -
**
** % cc -n -O creatdb.c error.c ../../lib/dbulib \
** ../../lib/access ../../lib/utility
** % chmod 4711 a.out
*/
# define MAXRELNS 20
# define MAXDBTEMP 2000
/* relation & attribute reln descriptors used in access methods */
extern DESC Reldes;
extern DESC Attdes;
extern int Status; /* user status, set by initucode */
DESC Btreesec; /* desc for btree sec. structure */
char *Fileset;
int Btree_fd; /* btree file */
int Dbstat; /* database status */
int Dbson, Dbsoff; /* same: bits turned on, bits turned off */
typedef struct reldes
{
int bitson;
int bitsoff;
PARM parmv[PV_MAXPC];
} RELDES;
RELDES Rellist[MAXRELNS];
char Delim;
extern char *Dbpath;
short tTdbu[100];
main(argc, argv)
int argc;
char *argv[];
{
register int i;
int admin;
char adminbuf[100];
extern struct admin Admin;
extern int errno;
auto int code;
struct relation relk;
char *database;
char **av;
register char *p;
char *user_ovrd;
int faterr;
register int *flagv;
char *dbtmpfile;
extern char *Parmvect[];
extern char *Flagvect[];
int exists;
int *flaglkup();
char *ztack();
extern char *rindex();
# ifdef rewinddir
DIR *dir_ptr; /* pointer to '.' */
struct direct *dir; /* directory entry */
# else rewinddir
struct dir direct;
# endif rewinddir
argv[argc] = NULL;
# ifdef xSTR1
tTrace(argv, 'T', tTdbu, 100);
# endif
/*
** Do a lot of magic initialization.
**
** 'exists' get set to -1 if the database does not exist
** at all, 1 if it exists, and 0 if it does not
** exist but there is an indirect pointer to it.
*/
exists = 0;
i = initucode(argc, argv, TRUE, NULL, -1);
switch (i)
{
case 0:
case 5:
exists = 1;
break;
case 6:
exists = 0;
case 1:
break;
case 2:
printf("You are not authorized to create this database\n");
exit(-1);
case 3:
printf("You are not a valid INGRES user\n");
exit(-1);
case 4:
printf("No database name specified\n");
usage:
printf("Usage: creatdb [-uname] [-e] [-m] [+-c] [+-q] dbname\n");
exit(-1);
default:
syserr("initucode %d", i);
}
faterr = 0;
dbtmpfile = 0;
for (av = Flagvect; (p = *av) != NULL; av++)
{
if (p[0] != '-' && p[0] != '+')
syserr("flag %s", p);
switch (p[1])
{
case 'F': /* dbtmplt file */
if (p[2] == 0)
goto badflag;
dbtmpfile = &p[2];
break;
case 'T': /* trace flag */
break;
default:
if (flagv = flaglkup(p[1], p[0]))
{
if (p[0] == '+')
*flagv = 1;
else
*flagv = -1;
continue;
}
badflag:
printf("bad flag %s\n", p);
faterr++;
continue;
}
if (p[0] == '+')
goto badflag;
}
/* check for legality of database name */
database = Parmvect[0];
if (Parmvect[1] != NULL)
{
printf("Too many parameters to creatdb");
goto usage;
}
if (!check(database))
{
printf("Illegal database name %s\n", database);
exit(-1);
}
if ((Status & U_CREATDB) == 0)
{
printf("You are not allowed this command\n");
exit(-1);
}
/* end of input checking */
if (faterr != 0)
exit(2);
/* now see if it should have been there */
if (flagval('m') || flagval('e'))
{
if (exists <= 0)
{
printf("Database %s does not exist\n", database);
exit(-1);
}
# ifdef xSTR3
if (tTf(1, 14))
printf("Dbpath = '%s'\n", Dbpath);
# endif
if (chdir(Dbpath) < 0)
syserr("chdir %s", Dbpath);
if (!flagval('e'))
{
# ifdef rewinddir
/* make certain that it is empty */
if ( (dir_ptr = opendir(".")) == NULL )
syserr(0,"Can't open '.'");
for ( dir = readdir(dir_ptr) ; dir != NULL ; dir = readdir(dir_ptr) )
{
if ( strcmp(".",dir->d_name) && strcmp("..",dir->d_name) )
syserr(0, "%s is not empty", database);
}
closedir(dir_ptr);
# else rewinddir
/* make certain that it is empty */
freopen(".", "r", stdin);
/* Skip "." and ".." entries */
fread(&direct, sizeof (struct dir), 1, stdin);
fread(&direct, sizeof (struct dir), 1, stdin);
while ( fread(&direct, sizeof (struct dir), 1, stdin) != EOF)
{
if ( direct.d_ino != 0)
syserr(0, "%s is not empty", database);
}
# endif rewinddir
}
else
{
/* check for correct owner */
acc_init();
if (!bequal(Usercode, Admin.adhdr.adowner, UCODE_SZ))
syserr(0, "You are not the DBA for this database");
}
}
else
{
if (exists > 0)
{
printf("Database %s already exists\n", database);
exit(-1);
}
/* create it */
i = fork();
if (i < 0)
syserr("fork err");
if (i == 0)
{
/* split off directory */
*(p = rindex(Dbpath, '/')) = '\0';
chdir(Dbpath);
if (setuid(getuid()))
syserr("setuid");
# ifndef xB_UNIX
if (setgid(getgid()))
syserr("setgid");
# endif
# ifdef xV7_UNIX
umask(00);
# endif
*p++ = '/';
execl("/bin/mkdir", "/bin/mkdir", p, 0);
execl("/usr/bin/mkdir", "/usr/bin/mkdir", p, 0);
syserr("exec /bin/mkdir");
}
while (wait(&code) != -1)
continue;
/* move into data/base directory */
if (chdir(Dbpath) < 0)
syserr("chdir %s: probably bad default mode in mkdir",
Dbpath);
/* change the mode of the database */
i = fork();
if (i < 0)
syserr("fork 2");
if (i == 0)
{
setuid(getuid());
if (chmod(".", 0777))
syserr("chmod");
exit(0);
}
while (wait(&code) != -1)
continue;
if ((code & I1MASK) != 0)
exit(code);
}
/* reset 'errno', set from possibly bad chdir */
errno = 0;
/* determine name of dbtmplt file and open */
if (dbtmpfile == NULL)
{
/* create dbtmplt file name */
dbtmpfile = ztack(ztack(Pathname, "/files/dbtmplt"), VERSION);
}
if (freopen(dbtmpfile, "r", stdin) == NULL)
syserr("dbtmplt open %s", dbtmpfile);
readdbtemp();
/* check for type -- update database status versus create */
if (flagval('e'))
changedb();
else
makedb();
/* close the cache descriptors */
# ifdef xSTR3
if (tTf(50, 0))
{
printf("Attdes.reltid = ");
dumptid(&Attdes.reltid);
printf("Reldes.reltid = ");
dumptid(&Reldes.reltid);
}
# endif
if (i = closer(&Attdes))
syserr("creatdb: closer(att) %d", i);
if (i = closer(&Reldes))
syserr("creatdb: closer(rel) %d", i);
/* bring tupcount in 'admin' file to date */
Admin.adreld.reldum.reltups = Reldes.reldum.reltups;
Admin.adattd.reldum.reltups = Attdes.reldum.reltups;
/* set other fields as appropriate */
Admin.adreld.relfp = Admin.adattd.relfp = -1;
Admin.adreld.relopn = Admin.adattd.relopn = 0;
Admin.adhdr.adlength = sizeof Admin.adhdr;
Admin.adhdr.adreldsz = Admin.adhdr.adattdsz = sizeof Admin.adreld;
Admin.adhdr.adversion = DBVERCODE;
if ((admin = creat("admin", FILEMODE)) < 0)
syserr("main: creat admin");
if (write(admin, &Admin, sizeof Admin) != sizeof Admin)
syserr("main: write Admin");
close(admin);
execl((ztack(Pathname, "/bin/sysmod")), "sysmod", database, 0);
/* exit successfully */
exit(0);
}
/*
** Rubout processing.
*/
rubproc()
{
exit(-2);
}
/*
** READDBTEMP -- read the dbtmplt file and build internal form
**
** This routine reads the dbtmplt file (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 dbtmplt file 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 QUEL.
**
** 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.
**
** Parameters:
** none
**
** Returns:
** none
**
** Side Effects:
** Dbstat gets the database status.
** Rellist is created with a list of the relations,
** (as parameter vectors -01:2st as passed to
** create). The entry after the last entry
** has its pv[0] == NULL.
**
** Called By:
** main
**
** Trace Flags:
** none
*/
readdbtemp()
{
static char buf[MAXDBTEMP];
register RELDES *r;
register PARM *q;
register int i;
int j;
char *p;
int defrstat;
auto int bitson, 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')
syserr("readdbtemp: bad Dbstat %c", 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].pv_type = PV_STR;
q[1].pv_val.pv_str = p;
p += getname(p) + 1;
/* test for end of dbtmplt file */
if (q[1].pv_val.pv_str[0] == 0)
break;
/* get relation status */
i = getstat(defrstat, &r->bitson, &r->bitsoff);
i |= S_CATALOG; /* guarantee system catalog */
q->pv_type = PV_STR;
q++->pv_val.pv_str = p;
*p++ = ((i >> 15) & 1) + '0';
for (j = 12; j >= 0; j -= 3)
*p++ = ((i >> j) & 07) + '0';
*p++ = 0;
q++;
if (Delim != '\n')
syserr("readdbtemp: bad rel %c", Delim);
/* read attribute names and formats */
for (;;)
{
/* get attribute name */
q->pv_type = PV_STR;
q++->pv_val.pv_str = p;
p += getname(p) + 1;
if (q[-1].pv_val.pv_str[0] == 0)
break;
if (Delim != '\t')
syserr("readdbtemp: bad att %s, d='%c'",
q[-1].pv_val.pv_str, Delim);
/* get attribute type */
q->pv_type = PV_STR;
q++->pv_val.pv_str = p;
p += getname(p) + 1;
if (Delim != '\n')
syserr("readdbtemp: bad type %c", Delim);
}
/* insert end of argv signal */
(--q)->pv_type = PV_EOF;
/* ad-hoc overflow test */
if (p >= &buf[MAXDBTEMP])
syserr("readdbtemp: overflow");
}
/* mark the end of list */
q[1].pv_type = PV_EOF;
}
/*
** GETSTAT -- Get status word
**
** A status word is read from the standard input (presumably
** 'dbtmplt'). 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.
**
** Side Effects:
** File activity.
**
** Called By:
** readdbtemp
**
** Trace Flags:
** none
*/
getstat(def, bitson, bitsoff)
int def;
int *bitson;
int *bitsoff;
{
register int c;
register int stat;
register int i;
int setbits;
int clrbits;
char ctlch;
/* reset bits being turned on and off */
*bitson = *bitsoff = 0;
/* check to see if a base status wolushs defined */
if (Delim == '\n' || (Delim = c = getchar()) < '0' || c > '7')
{
/* no, use default */
stat = def;
}
else
{
/* read base status field */
ungetc(c, stdin);
stat = roctal();
}
/* scan '+c+N' entries */
for (;;)
{
/* check for a flag present */
c = Delim;
if (c == '\n' || c == ':')
return (stat);
if (c != '+' && c != '-')
{
badfmt:
syserr("getstat: bad fmt %c", 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 */
c = getchar();
if (c != '+' && c != '-')
goto badfmt;
setbits = roctal();
/* test whether action on -X same as on +X */
if (Delim == '~')
{
/* they have different bits (see above) */
clrbits = roctal();
}
else
{
/* on 'creatdb -e -X', use opposite bits of +X */
clrbits = setbits;
}
/* test for any effect at all */
if (i == 0)
continue;
/* test whether we should process the '+N' */
if ((ctlch == '-') ? (i < 0) : (i > 0))
i = setbits;
else
{
i = clrbits;
/* switch sense of bit action */
if (c == '+')
c = '-';
else
c = '+';
}
if (c == '+')
{
stat |= i;
*bitson |= i;
}
else
{
stat &= ~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.
**
** Parameters:
** none
**
** Returns:
** value of octal number in the input stream.
**
** Side Effects:
** 'Delim' is set to the delimiter which terminated the
** number.
** File activity on stdin.
**
** Called By:
** getstat()
**
** Trace Flags:
** none
*/
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.
**
** Side Effects:
** File activity on standard input.
**
** Called By:
** readdbtemp
**
** Trace Flags:
** none
*/
getname(ptr)
char *ptr;
{
register int len;
register int c;
register char *p;
len = 0;
for (p = ptr; (c = getchar()) != EOF; len++)
{
/* check for end of name */
if ((c < 'a' || c > 'z') &&
(c < '0' || c > '9'))
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 admin file
** in the internal 'Admin' struct. This is the code which
** subsequently gets used by openr and opencatalog. Notice
** that the 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 'relation' and 'attribute'
** relation pages during the creates of the 'relation' and
** 'attribute' 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 relation relation for the
** admin cache (which of course should exist now). Thus,
** the closer's in main (which must be around to update the
** tuple count) will work right.
**
** Parameters:
** none
**
** Returns:
** none
**
** Side Effects:
** A database is created!!
** Several files will be created in the current directory,
** one for each relation mentioned in the
** 'dbtmplt' file.
** The 'Admin' struct will be filled in.
**
** Called By:
** main
**
** Trace Flags:
** 20
*/
makedb()
{
DESC d;
register RELDES *r;
# ifdef xSTR3
if (tTf(51, 0))
printf(">>makedb, Usercode = %s (%u)\n", Usercode, Usercode);
# endif
/* create the physical files */
for (r = Rellist; r->parmv[1].pv_type != PV_EOF; r++)
{
makefile(r);
}
/* initialize the admin file internal cache */
bmove(Usercode, Admin.adhdr.adowner, UCODE_SZ);
Admin.adhdr.adflags = Dbstat;
makeadmin(&Admin.adreld, Rellist[0].parmv);
makeadmin(&Admin.adattd, Rellist[1].parmv);
/* done with admin initialization */
/* initialize relations */
for (r = Rellist; r->parmv[1].pv_type != PV_EOF; r++)
{
makereln(r);
}
}
/*
** MAKEADMIN -- manually initialize descriptor for 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 admin file.
**
** Notice that the 'reltid' field is filled in sequentially.
** This means that the relations put into the 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 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.
**
** Returns:
** none
**
** Side Effects:
** none
**
** Called By:
** main
**
** Trace Flags:
** 10
*/
makeadmin(d, pv)
DESC *d;
PARM pv[];
{
register DESC *des;
register PARM *p;
register int i;
auto int len;
static int lineno;
char fname[MAXNAME + 3];
des = d;
p = pv;
# ifdef xSTR2
if (tTf(10, -1))
{
printf("creating %s in admin\n", p[1].pv_val.pv_str);
}
# endif
i = oatoi(p++->pv_val.pv_str);
ingresname(p++->pv_val.pv_str, Usercode, fname);
bmove(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.ltid = 0;
des->reltid.s_tupid.line_id = lineno++;
des->relfp = open(fname, O_RDWR);
if (des->relfp < 0)
syserr("makeadmin: open %s", fname);
des->relopn = (des->relfp + 1) * -5;
/* initialize domain info */
for (; p++->pv_type != PV_EOF; p++)
{
register char c;
c = p[0].pv_val.pv_str[0];
if (c != 'i' && c != 'c' && c != 'f')
syserr("dbtmplt: type err on %s", p[0].pv_val.pv_str);
des->relfrmt[++(des->reldum.relatts)] = c;
len = atoi(&p[0].pv_val.pv_str[1]);
des->relfrml[des->reldum.relatts] = len;
des->reloff[des->reldum.relatts] = 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).
**
** Returns:
** none
**
** Side Effects:
** A file with one page is created.
**
** Called By:
** makedb
** changedb
**
** Trace Flags:
** 12
*/
makefile(r)
register RELDES *r;
{
DESC d;
register int i;
ingresname(r->parmv[1].pv_val.pv_str, Usercode, d.reldum.relid);
# ifdef xSTR1
if (tTf(12, 0))
printf("creat %s\n", d.reldum.relid);
# endif
if ((d.relfp = creat(d.reldum.relid, FILEMODE)) < 0)
syserr("creat %s", d.reldum.relid);
if ((i = formatpg(&d, (long) 1)) != 0)
syserr("makefile: formatpg %d", i);
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.
**
** Returns:
** none
**
** Side Effects:
** Information will be inserted into the 'relation' and
** 'attribute' relations.
**
** Called By:
** makedb
** changedb
**
** Trace Flags:
** 1
*/
makereln(r)
register RELDES *r;
{
register int pc;
register PARM *pv;
int i;
pc = 0;
for (pv = r->parmv; pv->pv_type != PV_EOF; pv++)
pc++;
pv = r->parmv;
i = create(pc, pv);
if (i != 0)
syserr("create %d", 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:
** p -- the string to check.
**
** Returns:
** TRUE -- ok.
** FALSE -- failure.
**
** Side Effects:
** none
**
** Called By:
** main
**
** Trace Flags:
** none
*/
check(p)
char *p;
{
register char c;
/* check string length */
if (length(p) > 14)
return (FALSE);
/* check the first character of the string for alphabetic */
c = *p++;
if (c < 'a' || c > 'z')
return (FALSE);
/* check the rest for alphanumeric */
while ((c = *p++) != 0)
{
if (c == '_')
continue;
if (c >= '0' && c <= '9')
continue;
if (c >= 'a' && 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:
** NULL -- 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.
**
** Side Effects:
** none
**
** Called By:
** main
** flagval
**
** Trace Flags:
** none
*/
struct flag
{
char flagname; /* the name of the flag */
char flagtype; /* -1: -x form; +1: +x form; 0: both */
int 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
};
int *
flaglkup(flagname, plusminus)
char flagname;
char plusminus;
{
register char f;
register struct flag *p;
register char pm;
f = flagname;
pm = plusminus;
/* look up flag in vector */
for (p = Flags; p->flagname != f; p++)
{
if (p->flagname == 0)
return (NULL);
}
/* found in list -- check type */
if ((pm == '+' && p->flagtype < 0) ||
(pm == '-' && p->flagtype > 0))
return (NULL);
/* 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'.
**
** Side Effects:
** none
**
** Called By:
** readdbtemp()
** main()
**
** Trace Flags:
** none
*/
flagval(f)
register char f;
{
register char *p;
int *flaglkup();
/* get value of flag */
p = (char *)flaglkup(f, 0);
/* test for error return, syserr if so */
if (p == NULL)
syserr("flagval: flag %c", 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 syserr'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 relation relation.
**
** (4) If the relation being updated was the "relation" or
** "attribute" relation, we change the Admin 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 attribute or relation
** 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 attribute relation cache not
** because we use it, but because we want to do a closer
** in main() to insure that the tupcount is updated in all
** cases.
**
** Parameters:
** none
**
** Returns:
** none
**
** 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.
**
** Called By:
** main
**
** Trace Flags:
** 40
*/
changedb()
{
register RELDES *r;
struct relation relk, relt;
TID tid;
register int i;
# ifdef xSTR1
if (tTf(40, 0))
printf(">>> changedb: Dbson=%o, Dbsoff=%o\n", Dbson, Dbsoff);
# endif
/* check to see we aren't doing anything illegal */
if (flagval('q') < 0)
{
syserr(0, "I'm sorry, it is not possible to turn query modification off");
}
/* update the database status field */
Admin.adhdr.adflags = (Admin.adhdr.adflags | Dbson) & ~Dbsoff;
/* open the system catalog caches */
opencatalog("relation", OR_WRITE);
opencatalog("attribute", OR_READ);
/* scan the relation list:- Rellist */
for (r = Rellist; r->parmv[1].pv_type != PV_EOF; r++)
{
/* see if this relation exists */
clearkeys(&Reldes);
setkey(&Reldes, &relk, r->parmv[1].pv_val.pv_str, RELID);
i = getequal(&Reldes, &relk, &relt, &tid);
if (i < 0)
syserr("changedb: getequal");
if (i > 0)
{
/* doesn't exist, create it */
printf("Creating relation %s\n", r->parmv[1].pv_val.pv_str);
makefile(r);
makereln(r);
}
else
{
/* exists -- check to make sure it is the right one */
if ((relt.relstat & S_CATALOG) == 0)
{
/* exists as a user reln -- tough luck buster */
printf("Relation %s already exists -- I cannot bring this database\n", r->parmv[1].pv_val.pv_str);
printf(" up to date. Sorry.\n");
exit(3);
}
/* it exists and is the right one -- update status */
if (r->bitson == 0 && r->bitsoff == 0)
continue;
/* actual work need be done */
relt.relstat = (relt.relstat | r->bitson) & ~r->bitsoff;
/* replace tuple in relation relation */
i = replace(&Reldes, &tid, &relt, FALSE);
if (i != 0)
syserr("changedb: replace %d", i);
/* update Admin struct if "relation" or "attribute" */
if (sequal(r->parmv[1].pv_val.pv_str, "relation"))
Admin.adreld.reldum.relstat = relt.relstat;
else if (sequal(r->parmv[1].pv_val.pv_str, "attribute"))
Admin.adattd.reldum.relstat = relt.relstat;
}
}
}
/*
** READADMIN -- read the admin file into the 'Admin' cache
**
** This routine opens and reads the 'Admin' cache from the
** '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 'relation' and 'attribute' relations
** are opened, and the descriptors for them in the Admin
** struct are filled in with their file descriptors.
**
** Parameters:
** none
**
** Returns:
** none
**
** Side Effects:
** The 'Admin' struct is filled in.
** The 'relation...xx' and 'attribute...xx' files are
** opened.
**
** Called By:
** acc_init (accbuf.c)
** changedb
**
** Trace Flags:
** none
*/
readadmin()
{
register int i;
char relname[MAXNAME + 4];
/* read the stuff from the admin file */
if (flagval('e'))
{
i = open("admin", O_RDONLY);
if (i < 0)
syserr("readadmin: open admin %d", i);
checkadmin(i);
close(i);
/* open the physical files for 'relation' and 'attribute' */
ingresname("relation", Admin.adhdr.adowner, relname);
if ((Admin.adreld.relfp = open(relname, O_RDWR)) < 0)
syserr("readadmin: open `%.14s'", relname);
ingresname("attribute", Admin.adhdr.adowner, relname);
if ((Admin.adattd.relfp = open(relname, O_RDWR)) < 0)
syserr("readadmin: open `%.14s'", relname);
Admin.adreld.relopn = (Admin.adreld.relfp + 1) * -5;
Admin.adattd.relopn = (Admin.adattd.relfp + 1) * 5;
}
return (0);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.