# include "../ingres.h" # include "../aux.h" # include "../fileio.h" # include "../access.h" # include "../pipes.h" # include "../symbol.h" # include "../unix.h" /* ** 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 ** 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. ** ** Requires: ** Much. Notably, be careful about changes to any ** access methods, especially automatic references to ** to the 'admin' file or relation relation. ** create() -- to actually create relations. ** ** 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() ** ** Diagnostics: ** No database name specified. ** You may not access this database ** Your entry in the users file says this is ** not a valid database for you. ** You are not a valid INGRES user ** You do not have a users file entry, and can ** not do anything with INGRES at all. ** You are not allowed this command ** The U_CREATDB bit is not set in your users ** file entry. ** You may not use the -u flag ** Only the INGRES superuser may say s/he is ** someone else. ** %s does not exist ** With -e or -m, the directory does not exist. ** %s already exists ** Without either -e or -m, the database ** (actually, the directory) already exists. ** %s is not empty ** With the -m flag, the directory you named ** must be empty. ** You are not the DBA for this database ** With the -e flag, you must be the database ** administrator. ** ** Syserrs: ** fork ** A fork request (for the mkdir command) failed; ** probably if you try again later things will ** work ** setuid ** The call to setuid() (to reset the user id ** to that of the actual user) failed. This ** "cannot happen". ** setgid ** Same for setgid(). ** exec /bin/mkdir ** It was not possible to execute the mkdir ** command, used to create a directory for ** the database. ** chmod ** The chmod command on the database (after the ** return from mkdir) failed -- probably due ** to the mkdir command failing somehow before ** creating the directory. Notice that we do ** not check the return from mkdir, since we ** are not sure that it is unpolluted on all ** systems. ** chdir %s ** After creating the database, we could not ** change directory into it. ** open .../files/dbtmplt ** The dbtmplt file does not exist or is not ** readable. ** creatdb: closer(rel) %d ** It was not possible to close the relation ** relation after finishing the bulk of the ** creatdb. ** closer(att) %d ** Ditto for attribute relation. ** main: creat admin ** It was not possible to create the 'admin' ** file in the database. This is, of course, ** a total disaster. ** main: write Admin ** It was not possible to write the admin file ** out -- probably out of disk space. ** ** Compilation Instructions: ** % setup creatdb ** ** - which translates to - ** ** % cc -n -O creatdb.c error.c ../../lib/dbulib \ ** ../../lib/access ../../lib/utility ** % chmod 4711 a.out ** ** History: ** 8/15/79 (eric) (6.2/7) -- changed 'mkdir' stuff to ** change into /mnt/ingres/data/base and then ** mkdir on 'database' instead of 'Dbpath' -- ** to avoid dependence on a mkdir bug on our ** system. Changed to assume that argv[argc] ** is NULL instead of -1 for v7. Added #include ** of unix.h to get unix version flags. ** 10/11/78 (eric) -- -F option added. ** 8/3/78 (eric) -- written. */ # define MAXRELNS 20 # define MAXDBTEMP 2000 /* relation & attribute reln descriptors used in access methods */ extern struct descriptor Reldes; extern struct descriptor Attdes; extern int Status; /* user status, set by initucode */ int Dbstat; /* database status */ int Dbson, Dbsoff; /* same: bits turned on, bits turned off */ struct reldes { int bitson; int bitsoff; char *parmv[MAXPARMS]; }; struct reldes Rellist[MAXRELNS]; char Delim; extern char *Dbpath; main(argc, argv) int argc; char *argv[]; { register int i; FILE *admin; char adminbuf[IOBUFSIZ]; 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; extern char Version[]; # ifdef xSTR1 tTrace(&argc, argv, 'T'); # endif /* ** Do a lot of magic initialization. */ exists = 0; i = initucode(argc, argv, TRUE, NULL, -1); switch (i) { case 0: case 5: exists = 1; break; case 6: exists = -1; 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 (database[1]) { case 'F': /* dbtmplt file */ if (p[2] == 0) goto badflag; dbtmpfile = &p[2]; 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')) { # ifdef xSTR3 if (tTf(1, 14)) printf("Dbpath = '%s'\n", Dbpath); # endif if (flagval('e') && exists <= 0) { printf("Database %s does not exist\n", database); exit(-1); } if (chdir(Dbpath) < 0) { printf("Directory for %s does not exist\n", database); exit(-1); } if (!flagval('e')) { /* make certain that it is empty */ freopen(".", "r", stdin); for (i = 0; i < 16; i++) getw(stdin); while ((i = getw(stdin)) != EOF) { if (i != 0) syserr(0, "%s is not empty", database); for (i = 0; i < 7; i++) getw(stdin); } } else { /* check for correct owner */ acc_init(); if (!bequal(Usercode, Admin.adhdr.adowner, 2)) 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) { /* enter database directory */ initdbpath(NULL, adminbuf, FALSE); if (chdir(adminbuf) < 0) syserr("%s: cannot enter", adminbuf); /* arrange for proper permissions */ if (setuid(getuid())) syserr("setuid"); # ifndef xB_UNIX if (setgid(getgid())) syserr("setgid"); # endif execl("/bin/mkdir", "mkdir", database, 0); syserr("exec /bin/mkdir"); } while (wait(&code) != -1) continue; if (chdir(Dbpath) < 0) syserr("could not chdir into %s; probably bad default mode in '/bin/mkdir'", Dbpath); i = fork(); if (i < 0) syserr("fork 2"); if (i == 0) { setuid(getuid()); if (chmod(".", 0777)) syserr("chmod"); exit(0); } while (wait(&code) != -1) ; if ((code & 0377) != 0) exit(code); } /* reset 'errno', set from possibly bad chdir */ errno = 0; /* determine name of dbtmplt file and open */ if (dbtmpfile == NULL) { smove(Version, adminbuf); for (i = 0; adminbuf[i] != 0; i++) if (adminbuf[i] == '/') break; adminbuf[i] = 0; dbtmpfile = ztack(ztack(Pathname, "/files/dbtmplt"), adminbuf); } 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 */ bmove(&Reldes.reltups, &Admin.adreld.reltups, sizeof Reldes.reltups); bmove(&Attdes.reltups, &Admin.adattd.reltups, sizeof Attdes.reltups); /* clean up some of the fields to catch problems later */ Admin.adreld.relfp = Admin.adattd.relfp = -1; Admin.adreld.relopn = Admin.adattd.relopn = 0; if ((admin = creat("admin", FILEMODE)) < 0) syserr("main: creat admin"); if (write(admin, &Admin, sizeof Admin) != sizeof Admin) syserr("main: write Admin"); close(admin); /* 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 -- just as passed to ** create). The entry after the last entry ** has its pv[0] == NULL. ** ** Requires: ** getstat -- to read status words. ** getname -- to read names. ** Delim -- set to the delimiter which terminated getstat ** or getname. ** ** Called By: ** main ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** readdbtemp: bad Dbstat -- the Dbstat entry was not ** terminated with a newline. ** readdbtemp: bad rel delim -- a relation name entry ** did not terminate with a newline. ** readdbtemp: bad att delim -- an attribute entry ** did not terminate with a colon. ** readdbtemp: bad type delim -- an attribute type entry ** did not end with a newline. */ readdbtemp() { static char buf[MAXDBTEMP]; register struct reldes *r; register char **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] = p; p =+ getname(p) + 1; /* test for end of dbtmplt file */ if (q[1][0] == 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') syserr("readdbtemp: bad rel %c", Delim); /* read attribute names and formats */ for (;;) { /* get attribute name */ *q++ = p; p =+ getname(p) + 1; if (q[-1][0] == 0) break; if (Delim != '\t') syserr("readdbtemp: bad att %c", Delim); /* get attribute type */ *q++ = p; p =+ getname(p) + 1; if (Delim != '\n') syserr("readdbtemp: bad type %c", Delim); } /* insert end of argv signal */ *--q = NULL; /* ad-hoc overflow test */ if (p >= &buf[MAXDBTEMP]) syserr("readdbtemp: overflow"); } /* mark the end of list */ q[1] = NULL; } /* ** 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. ** ** Requires: ** getchar -- to get input characters ** roctal -- to read an octal number. ** ungetc -- to give a character back during parsing. ** Delim -- set the the terminating delimitor in roctal(). ** flagval() -- to get the value of a flag. ** ** Called By: ** readdbtemp ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** getstat: bad fmt %c -- a '+' or '-' was expected. */ 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 word is 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. ** ** Requires: ** getchar() -- to get the input characters. ** Delim -- as noted above. ** ** Called By: ** getstat() ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** 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. ** ** Requires: ** getchar() ** Delim ** ** Called By: ** readdbtemp ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** 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. ** ** Requires: ** makefile -- to create the physical file. ** makereln -- to create the relations. ** Rellist -- containing the list of relations to be ** created with specs. ** makeadmin -- to initialize the Admin struct. ** ** Called By: ** main ** ** Trace Flags: ** 20 ** ** Diagnostics: ** none ** ** Syserrs: ** open(rel) ** It was not possible to open the relation ** relation for the Admin.adreld cache. */ makedb() { struct descriptor d; register struct reldes *r; register int pc; register char **pv; # 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] != 0; r++) { makefile(r); } /* initialize the admin file internal cache */ bmove(Usercode, Admin.adhdr.adowner, 2); 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] != 0; 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 ** ** Requires: ** ingresname ** oatoi ** atoi ** typeconv ** ** Called By: ** main ** ** Trace Flags: ** 10 ** ** Diagnostics: ** none ** ** Syserrs: ** makeadmin: type err %c -- a bad type specifier ** occured in the dbtmplt file. ** makeadmin: len err %s -- a length was specified ** which was not an integer. */ makeadmin(d, pv) struct descriptor *d; char *pv[]; { register struct descriptor *des; register char **p; register int i; auto int len; static int tid; char fname[MAXNAME + 3]; des = d; p = pv; # ifdef xSTR2 if (tTf(10, -1)) printf("creating %s in admin\n", p[1]); # endif i = oatoi(*p++); ingresname(*p++, Usercode, fname); bmove(fname, des->relid, MAXNAME + 2); des->relstat = i; des->relatts = 0; des->relwid = 0; des->relspec = M_HEAP; des->reltid = tid++; des->relfp = open(fname, 2); if (des->relfp < 0) syserr("makeadmin: open %s", fname); des->relopn = (des->relfp + 1) * -5; /* initialize domain info */ for (; *p++ != NULL; p++) { i = typeconv(p[0][0]); if (i < 0) syserr("dbtmplt: type err %c", p[0][0]); des->relfrmt[++(des->relatts)] = i; if (atoi(&p[0][1], &len) != 0) syserr("makeadmin: len err %s", p[0]); des->relfrml[des->relatts] = len; des->reloff[des->relatts] = des->relwid; des->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. ** ** Requires: ** ingresname -- to create the actual file name. ** formatpg -- to put the single page into the relation. ** creat -- to create the file ** FILEMODE -- the mode of the created file. ** ** Called By: ** makedb ** changedb ** ** Trace Flags: ** 12 ** ** Diagnostics: ** none ** ** Syserrs: ** creat %s ** The file named could not be created. ** formatpg ** The 'formatpg' call failed; check the ** access method error for why. */ makefile(rr) struct reldes *rr; { register struct reldes *r; struct descriptor d; long npages; r = rr; ingresname(r->parmv[1], Usercode, d.relid); # ifdef xSTR1 if (tTf(12, 0)) printf("creat %s\n", d.relid); # endif if ((d.relfp = creat(d.relid, FILEMODE)) < 0) syserr("creat %s", d.relid); npages = 1; if (formatpg(&d, npages)) syserr("formatpg"); 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. ** ** Requires: ** Rellist -- with a list of relations to be created ** and data descriptions. ** create -- to actually do the create. ** 'Admin' must be filled in. Openr must use 'Admin' ** and never look at the physical 'admin' file. ** ** Called By: ** makedb ** changedb ** ** Trace Flags: ** 1 ** ** Diagnostics: ** none ** ** Syserrs: ** create %d ** The call to 'create' failed, for reason ** given. */ makereln(rr) struct reldes *rr; { register struct reldes *r; register int pc; register char **pv; int i; r = rr; pc = 0; for (pv = r->parmv; *pv != NULL; pv++) pc++; pv = r->parmv; # ifdef xSTR1 if (tTf(1, 0)) prargs(pc, pv); # endif 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: ** st -- the string to check. ** ** Returns: ** TRUE -- ok. ** FALSE -- failure. ** ** Side Effects: ** none ** ** Requires: ** length -- to check the length of the string. ** ** Called By: ** main ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** none */ check(st) char *st; { register char c; register char *p; p = st; /* 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 ** ** Requires: ** Flags vector -- of type 'struct flag', defined below. ** This vector should be terminated with a ** zero 'flagname'. ** ** Called By: ** main ** flagval ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** 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: ** fx -- the flag to look up (see flaglkup). ** ** Returns: ** The value of flag 'fx'. ** ** Side Effects: ** none ** ** Requires: ** flaglkup() ** ** Called By: ** readdbtemp() ** main() ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** flagval: flag %c -- the value of a flag which does ** not exist was asked for. */ flagval(fx) char fx; { register char f; register char *p; f = fx; /* get value of flag */ p = 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. ** ** Requires: ** Dbson, Dbsoff -- bits to set/clear for the database ** status. ** opencatalog() -- to open the relation relation. ** getequal() -- to get the relation relation tuple ** for specific relations. ** Rellist -- a list of needed relations and their ** data description. ** Reldes -- result of opencatalog() call. ** replace() -- to update the relstat in the relation ** relation for entries. ** Admin struct -- must be filled in with the current ** contents of the 'admin' file. ** makefile(), makereln() -- to make the relation ** if it does not exist at all. ** ** Called By: ** main ** ** Trace Flags: ** 40 ** ** Diagnostics: ** Relation %s already exists ** A system catalog which must be created does ** already exists as a user relation. ** I'm sorry, it is not possible to turn query modification off. ** The user tried to turn query modification off. ** We can't allow this because it could cause ** a syserr later; for instance, if the user ** referenced a view which might still hang around ** the 'relation' catalog. This would result in ** an attempt to open the file for the view, which ** of course does not exist. BANG! ** ** Syserrs: ** getequal ** getequal() failed. ** replace %d ** replace() failed. */ changedb() { register struct reldes *r; struct relation relk, relt; struct tup_id 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", 2); opencatalog("attribute", 0); /* scan the relation list:- Rellist */ for (r = &Rellist; r->parmv[1] != 0; r++) { /* see if this relation exists */ clearkeys(&Reldes); setkey(&Reldes, &relk, r->parmv[1], 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]); 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]); 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], "relation")) Admin.adreld.relstat = relt.relstat; else if (sequal(r->parmv[1], "attribute")) Admin.adattd.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. ** ** Requires: ** 'Admin' struct. ** 'Usercode' must have the user code of the DBA. ** ingresname() -- to create the physical file name. ** flagval() -- to test the -e flag. ** ** Called By: ** acc_init (accbuf.c) ** changedb ** ** Trace Flags: ** none ** ** Diagnostics: ** none ** ** Syserrs: ** adminread: open admin ** It was not possible to open the admin file. ** adminread: read admin ** There was a read error when reading the 'admin' ** file. ** adminread: open rel ** It was not possible to open the 'relation..xx' ** file. ** adminread: open att ** It was not possible to open the 'attribute..xx' ** file. */ readadmin() { register int i; char relname[MAXNAME + 4]; /* read the stuff from the admin file */ if (flagval('e')) { i = open("admin", 0); if (i < 0) syserr("readadmin: open admin %d", i); if (read(i, &Admin, sizeof Admin) != sizeof Admin) syserr("readadmin: read err admin"); close(i); /* open the physical files for 'relation' and 'attribute' */ ingresname(Admin.adreld.relid, Admin.adreld.relowner, relname); if ((Admin.adreld.relfp = open(relname, 2)) < 0) syserr("readadmin: open rel %d", Admin.adreld.relfp); ingresname(Admin.adattd.relid, Admin.adattd.relowner, relname); if ((Admin.adattd.relfp = open(relname, 2)) < 0) syserr("readadmin: open att %d", Admin.adattd.relfp); Admin.adreld.relopn = (Admin.adreld.relfp + 1) * -5; Admin.adattd.relopn = (Admin.adattd.relfp + 1) * 5; } return (0); }