4.3BSD/usr/ingres/source/dbu/create.c
# include <pv.h>
# include <ingres.h>
# include <access.h>
# include <aux.h>
# include <catalog.h>
# include <symbol.h>
# include <lock.h>
# include <func.h>
# include <sccs.h>
# include <errors.h>
SCCSID(@(#)create.c 8.6 2/8/85)
extern short tTdbu[];
extern int create();
extern int null_fn();
struct fn_def CreateFn =
{
"CREATE",
create,
null_fn,
null_fn,
NULL,
0,
tTdbu,
100,
'Z',
0
};
/*
** CREATE -- create new relation
**
** This module creates a brand new relation in the current
** directory (database). The relation is always created as
** a paged heap. It may not redefine an existing relation,
** or rename a system catalog.
**
** Trace Flags:
** 31
*/
struct domain
{
char *name;
char frmt;
char frml;
};
/*
** CREATE -- create new relation
**
** This routine is the driver for the create module.
**
** Parameters:
** pc -- parameter count
** pv -- parameter vector:
** 0 -- relation status (relstat) -- stored into
** the 'relstat' field in the relation
** relation, and used to determine the
** caller. Interesting bits are:
**
** S_INDEX -- means called by the index
** processor. If set, the 'relindxd'
** field will also be set to -1
** (SECINDEX) to indicate that this
** relation is a secondary index.
** S_CATALOG -- this is a system catalog.
** If set, this create was called
** from creatdb, and the physical
** file is not created. Also, the
** expiration date is set infinite.
** S_VIEW -- this is a view. Create has
** been called by the 'define'
** statement, rather than the
** 'create' statement. The physical
** file is not created.
**
** 1 -- relation name.
** 2 -- attname1
** 3 -- format1
** 4, etc -- attname, format pairs.
**
** Returns:
** zero -- successful create.
** else -- failure somewhere.
**
** Side Effects:
** A relation is created (this is a side effect?). This
** means entries in the 'relation' and 'attribute' cata-
** logs, and (probably) a physical file somewhere, with
** one page already in it.
**
** Trace Flags:
** 31
*/
create(pc, pv)
int pc;
PARM pv[];
{
register PARM *pp;
register int i;
int bad;
struct domain domain[MAXDOM];
struct domain *dom;
char *relname, tempname[MAXNAME+3];
struct tup_id tid;
struct relation rel, key;
struct attribute att;
DESC desr;
extern char *Usercode;
extern DESC Reldes, Attdes;
extern int errno;
register int relstat;
long temptid;
long npages;
int fdes;
bool internal;
# ifdef xZTR1
if (tTf(31, -1))
{
printf("creating %s\n", pv[1].pv_val.pv_str);
}
# endif
pp = pv;
relstat = oatoi(pp[0].pv_val.pv_str);
/*
** If this database has query modification, then default
** to denial on all user relations.
** (Since views cannot be protected, this doesn't apply to them)
*/
if ((Admin.adhdr.adflags & A_QRYMOD) && ((relstat & (S_VIEW || S_CATALOG)) == 0))
relstat |= (S_PROTALL | S_PROTRET);
relname = (++pp)->pv_val.pv_str;
internal = bequal(relname, "_SYS", 4);
ingresname(relname, Usercode, rel.relid);
bmove(rel.relid, att.attrelid, MAXNAME + 2);
opencatalog("relation", OR_WRITE);
/* check for duplicate relation name */
if ((relstat & S_CATALOG) == 0)
{
if (openr(&desr, OR_RELTID, relname) == 0)
{
if (bequal(desr.reldum.relowner, rel.relowner, 2))
{
return (error(DUPRELNAME, relname, 0)); /* bad relname */
}
if (desr.reldum.relstat & S_CATALOG)
{
return (error(SYSRELNAME, relname, 0)); /* attempt to rename system catalog */
}
}
}
opencatalog("attribute", OR_WRITE);
/* initialize structures for system catalogs */
initstructs(&att, &rel);
rel.relstat = relstat;
if ((relstat & S_CATALOG) != 0)
rel.relsave = 0;
else if ((relstat & S_INDEX) != 0)
rel.relindxd = SECINDEX;
# ifdef xZTR3
if (tTf(31, 2))
{
printf("\nrel->relprim = %D\n", rel.relprim);
printup(&Reldes, &rel);
}
# endif
/* check attributes */
pp++;
for (i = pc - 2; i > 0; i -= 2)
{
bad = chk_att(&rel, pp[0].pv_val.pv_str, pp[1].pv_val.pv_str, domain, internal);
if (bad != 0)
{
return (error(bad, relname, pp[0].pv_val.pv_str, pp[1].pv_val.pv_str, 0));
}
pp += 2;
}
/*
** Create files if appropriate. Concurrency control for
** the create depends on the actual file. To prevent
** to users with the same usercode from creating the
** same relation at the same time, their is check
** on the existence of the file. The important events are
** (1) if a tuple exists in the relation relation then
** the relation really exists. (2) if the file exists then
** the relation is being created but will not exist for
** use until the relation relation tuple is present.
** For VIEWS, the file is used for concurrency control
** during the create but is removed afterwards.
*/
if ((relstat & S_CATALOG) == 0)
{
/* for non system named temporary relations
** set a critical section lock while checking the
** existence of a file. If it exists, error return(DUPRELNAME)
** else create file.
*/
temptid = 0;
if (Lockrel && (!bequal(rel.relid,"_SYS",4)))
{
temptid = -1;
setcsl(temptid); /* set critical section lock */
if ((fdes = open(rel.relid,O_RDONLY)) >= 0)
{
/* file already exists */
close(fdes);
unlcs(temptid); /* release critical section lock */
return (error(DUPRELNAME, relname, 0));
}
errno = 0; /* file doesn't exist */
}
ingresname(rel.relid, rel.relowner, tempname);
desr.relfp = creat(tempname, FILEMODE);
if (temptid != 0)
unlcs(temptid); /* release critical section lock */
if (desr.relfp < 0)
syserr("create: creat %s", rel.relid);
desr.reltid.ltid = -1L; /* init reltid to unused */
if ((relstat & S_VIEW) == 0)
{
npages = 1;
if (i = formatpg(&desr, npages))
syserr("create: formatpg %d", i);
}
close(desr.relfp);
}
/* insert attributes into attribute relation */
pp = pv + 2;
dom = domain;
for (i = pc - 2; i > 0; i -= 2)
{
ins_att(&Attdes, &att, dom++);
pp += 2;
}
/*
** Flush the attributes. This is necessary for recovery reasons.
** If for some reason the relation relation is flushed and the
** machine crashes before the attributes are flushed, then recovery
** will not detect the error.
** The call below cannot be a "noclose" without major changes to
** creatdb.
*/
if (i = pageflush(0))
syserr("create:flush att %d", i);
if (i = insert(&Reldes, &tid, &rel, FALSE))
syserr("create: insert(rel, %.14s) %d", rel.relid, i);
if (relstat & S_VIEW)
unlink(tempname);
return (0);
}
/*
** CHK_ATT -- check attribute for validity
**
** The attribute is checked to see if
** * it's name is ok (within MAXNAME bytes)
** * it is not a duplicate name
** * the format specified is legal
** * there are not a ridiculous number of attributes
** (ridiculous being defined as anything over MAXDOM - 1)
** * the tuple is not too wide to fit on one page
**
** Parameters:
** rel -- relation relation tuple for this relation.
** attname -- tentative name of attribute.
** format -- tentative format for attribute.
** domain -- a 'struct domain' used to determine dupli-
** cation, and to store the resulting name and
** format in.
**
** Returns:
** zero -- OK
** 5104 -- bad attribute name.
** 5105 -- duplicate attribute name.
** 5106 -- bad attribute format.
** 5107 -- too many attributes.
** 5108 -- tuple too wide.
**
** Side Effects:
** 'rel' has the relatts and relwid fields updated to
** reflect the new attribute.
**
** Trace Flags:
** 31
*/
chk_att(rel, attname, format, domain, internal)
struct relation *rel;
char *attname, *format;
struct domain domain[];
bool internal;
{
register int i;
register struct relation *r;
r = rel;
# ifdef xZTR3
if (tTf(31, 1))
printf("chk_att %s %s\n", attname, format);
# endif
if (sequal(attname, "tid"))
return (BADATTRNAME); /* bad attribute name */
if ((i = dup_att(attname, r->relatts, domain)) < 0)
return (DUPATTRNAME); /* duplicate attribute */
if (formck(format, &domain[i], internal))
return (BADATTRFORMAT); /* bad attribute format */
r->relatts++;
r->relwid += domain[i].frml & I1MASK;
if (r->relatts >= MAXDOM)
return (TOOMANYDOMS); /* too many attributes */
if (r->relwid > MAXTUP && (r->relstat & S_VIEW) == 0)
return (RELTOOWIDE); /* tuple too wide */
return (0);
}
/*
** INS_ATT -- insert attribute into attribute relation
**
** Parameters:
** des -- relation descriptor for the attribute catalog.
** att -- attribute tuple, preinitialized with all sorts
** of good stuff (everything except 'attname',
** 'attfrmt', and 'attfrml'; 'attid' and 'attoff'
** must be initialized to zero before this routine
** is called the first time.
** dom -- 'struct domain' -- the information needed about
** each domain.
**
** Returns:
** none
**
** Side Effects:
** The 'att' tuple is updated in the obvious ways.
** A tuple is added to the 'attribute' catalog.
**
** Trace Flags:
** none currently
*/
ins_att(des, att, dom)
DESC *des;
struct attribute *att;
struct domain *dom;
{
register int i;
struct tup_id tid;
register struct domain *d;
d = dom;
pmove(d->name, att->attname, MAXNAME, ' ');
att->attfrmt = d->frmt;
att->attfrml = d->frml;
att->attid++;
if (insert(des, &tid, att, FALSE))
syserr("ins_att: insert(att, %s)", d->name);
att->attoff += att->attfrml & I1MASK;
}
/*
** DUP_ATT -- check for duplicate attribute
**
** The attribute named 'name' is inserted into the 'attalias'
** vector at position 'count'. 'Count' should be the count
** of existing entries in 'attalias'. 'Attalias' is checked
** to see that 'name' is not already present.
**
** Parameters:
** name -- the name of the attribute.
** count -- the count of attributes so far.
** domain -- 'struct domain' -- the list of domains
** so far, names and types.
**
** Returns:
** -1 -- attribute name is a duplicate.
** else -- index in 'domain' for this attribute (also
** the attid).
**
** Side Effects:
** The 'domain' vector is extended.
**
** Trace Flags:
** none
*/
dup_att(name, count, domain)
char *name;
int count;
struct domain domain[];
{
register struct domain *d;
register int lim;
register int i;
lim = count;
d = domain;
for (i = 0; i < lim; i++)
if (sequal(name, d++->name))
return (-1);
if (count < MAXDOM)
d->name = name;
return (i);
}
/*
** INITSTRUCTS -- initialize relation and attribute tuples
**
** Structures containing images of 'relation' relation and
** 'attribute' relation tuples are initialized with all the
** information initially needed to do the create. Frankly,
** the only interesting part is the the expiration date
** computation; longconst(9, 14976) is exactly the number
** of seconds in one week.
**
** Parameters:
** att -- attribute relation tuple.
** rel -- relation relation tuple.
**
** Returns:
** none
**
** Side Effects:
** 'att' and 'rel' are initialized.
**
** Requires:
** time -- to get the current date.
**
** Called By:
** create
**
** Trace Flags:
** none
**
** Diagnostics:
** none
**
** Syserrs:
** none
**
** History:
** 2/27/78 (eric) -- documented.
*/
initstructs(att, rel)
register struct attribute *att;
register struct relation *rel;
{
/* setup expiration date (today + one week) */
time(&rel->relstamp);
rel->relsave = rel->relstamp + 604800L;
rel->relfree = 0;
rel->reltups = 0;
rel->relatts = 0;
rel->relwid = 0;
rel->relprim = 1;
rel->relspec = M_HEAP;
rel->relindxd = 0;
rel->reldim = 0;
att->attxtra = 0;
att->attid = 0;
att->attoff = 0;
}
/*
** CHECK ATTRIBUTE FORMAT AND CONVERT
**
** The string 'a' is checked for a valid attribute format
** and is converted to internal form.
**
** zero is returned if the format is good; one is returned
** if it is bad. If it is bad, the conversion into a is not
** made.
**
** A format of CHAR can be length zero only if this
** create was generated internally.
*/
formck(a, dom, internal)
char *a;
struct domain *dom;
bool internal;
{
int len;
register int i;
char c;
register char *p;
register struct domain *d;
p = a;
c = *p++;
d = dom;
len = atoi(p);
i = len;
switch (c)
{
case INT:
if (i == 1 || i == 2 || i == 4)
{
d->frmt = INT;
d->frml = i;
return (0);
}
return (1);
case FLOAT:
if (i == 4 || i == 8)
{
d->frmt = FLOAT;
d->frml = i;
return (0);
}
return (1);
/* note: should disallow c0 from user (but needed internally) */
case CHAR:
if (i > MAXFIELD || i < 0 || (i == 0 && !internal))
return (1);
d->frmt = CHAR;
d->frml = i;
return (0);
}
return (1);
}