diff options
Diffstat (limited to 'src/backend/commands/creatinh.c')
-rw-r--r-- | src/backend/commands/creatinh.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c new file mode 100644 index 00000000000..a0e3a9f682b --- /dev/null +++ b/src/backend/commands/creatinh.c @@ -0,0 +1,564 @@ +/*------------------------------------------------------------------------- + * + * creatinh.c-- + * POSTGRES create/destroy relation with inheritance utility code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> +#include "postgres.h" + +#include "tcop/tcopdebug.h" + +#include "utils/builtins.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "nodes/pg_list.h" +#include "nodes/primnodes.h" +#include "nodes/plannodes.h" +#include "nodes/parsenodes.h" +#include "nodes/execnodes.h" + +#include "utils/syscache.h" +#include "utils/relcache.h" +#include "catalog/catname.h" +#include "catalog/pg_type.h" +#include "catalog/pg_inherits.h" +#include "catalog/pg_ipl.h" +#include "parser/catalog_utils.h" + +#include "commands/creatinh.h" + +#include "access/tupdesc.h" +#include "access/heapam.h" +#include "access/xact.h" + +/* ---------------- + * local stuff + * ---------------- + */ + +static int checkAttrExists(char *attributeName, + char *attributeType, List *schema); +static List *MergeAttributes(List *schema, List *supers); +static void StoreCatalogInheritance(Oid relationId, List *supers); + +/* ---------------------------------------------------------------- + * DefineRelation -- + * Creates a new relation. + * ---------------------------------------------------------------- + */ +void +DefineRelation(CreateStmt *stmt) +{ + char *relname = stmt->relname; + List *schema = stmt->tableElts; + int numberOfAttributes; + Oid relationId; + char archChar; + List *inheritList = NULL; + char *archiveName = NULL; + TupleDesc descriptor; + int heaploc, archloc; + + char* typename = NULL; /* the typename of this relation. not useod for now */ + + if ( strlen(relname) > NAMEDATALEN) + elog(WARN, "the relation name %s is > %d characters long", relname, + NAMEDATALEN); + + /* ---------------- + * Handle parameters + * XXX parameter handling missing below. + * ---------------- + */ + inheritList = stmt->inhRelnames; + + /* ---------------- + * determine archive mode + * XXX use symbolic constants... + * ---------------- + */ + archChar = 'n'; + + switch (stmt->archiveType) { + case ARCH_NONE: + archChar = 'n'; + break; + case ARCH_LIGHT: + archChar = 'l'; + break; + case ARCH_HEAVY: + archChar = 'h'; + break; + default: + elog(WARN, "Botched archive mode %d, ignoring", + stmt->archiveType); + break; + } + + if (stmt->location == -1) + heaploc = 0; + else + heaploc = stmt->location; + + /* + * For now, any user-defined relation defaults to the magnetic + * disk storgage manager. --mao 2 july 91 + */ + if (stmt->archiveLoc == -1) { + archloc = 0; + } else { + if (archChar == 'n') { + elog(WARN, "Set archive location, but not mode, for %s", + relname); + } + archloc = stmt->archiveLoc; + } + + /* ---------------- + * generate relation schema, including inherited attributes. + * ---------------- + */ + schema = MergeAttributes(schema, inheritList); + + numberOfAttributes = length(schema); + if (numberOfAttributes <= 0) { + elog(WARN, "DefineRelation: %s", + "please inherit from a relation or define an attribute"); + } + + /* ---------------- + * create a relation descriptor from the relation schema + * and create the relation. + * ---------------- + */ + descriptor = BuildDescForRelation(schema, relname); + relationId = heap_create(relname, + typename, + archChar, + heaploc, + descriptor); + + StoreCatalogInheritance(relationId, inheritList); + + /* ---------------- + * create an archive relation if necessary + * ---------------- + */ + if (archChar != 'n') { + /* + * Need to create an archive relation for this heap relation. + * We cobble up the command by hand, and increment the command + * counter ourselves. + */ + + CommandCounterIncrement(); + archiveName = MakeArchiveName(relationId); + + relationId = heap_create(archiveName, + typename, + 'n', /* archive isn't archived */ + archloc, + descriptor); + + pfree(archiveName); + } +} + +/* + * RemoveRelation -- + * Deletes a new relation. + * + * Exceptions: + * BadArg if name is invalid. + * + * Note: + * If the relation has indices defined on it, then the index relations + * themselves will be destroyed, too. + */ +void +RemoveRelation(char *name) +{ + AssertArg(name); + heap_destroy(name); +} + + +/* + * MergeAttributes -- + * Returns new schema given initial schema and supers. + * + * + * 'schema' is the column/attribute definition for the table. (It's a list + * of ColumnDef's.) It is destructively changed. + * 'inheritList' is the list of inherited relations (a list of Value(str)'s). + * + * Notes: + * The order in which the attributes are inherited is very important. + * Intuitively, the inherited attributes should come first. If a table + * inherits from multiple parents, the order of those attributes are + * according to the order of the parents specified in CREATE TABLE. + * + * Here's an example: + * + * create table person (name text, age int4, location point); + * create table emp (salary int4, manager char16) inherits(person); + * create table student (gpa float8) inherits (person); + * create table stud_emp (percent int4) inherits (emp, student); + * + * the order of the attributes of stud_emp is as follow: + * + * + * person {1:name, 2:age, 3:location} + * / \ + * {6:gpa} student emp {4:salary, 5:manager} + * \ / + * stud_emp {7:percent} + */ +static List * +MergeAttributes(List *schema, List *supers) +{ + List *entry; + List *inhSchema = NIL; + + /* + * Validates that there are no duplications. + * Validity checking of types occurs later. + */ + foreach (entry, schema) { + List *rest; + ColumnDef *coldef = lfirst(entry); + + foreach (rest, lnext(entry)) { + /* + * check for duplicated relation names + */ + ColumnDef *restdef = lfirst(rest); + + if (!strcmp(coldef->colname, restdef->colname)) { + elog(WARN, "attribute \"%s\" duplicated", + coldef->colname); + } + } + } + foreach (entry, supers) { + List *rest; + + foreach (rest, lnext(entry)) { + if (!strcmp(strVal(lfirst(entry)), strVal(lfirst(rest)))) { + elog(WARN, "relation \"%s\" duplicated", + strVal(lfirst(entry))); + } + } + } + + /* + * merge the inherited attributes into the schema + */ + foreach (entry, supers) { + char *name = strVal(lfirst(entry)); + Relation relation; + List *partialResult = NIL; + AttrNumber attrno; + TupleDesc tupleDesc; + + relation = heap_openr(name); + if (relation==NULL) { + elog(WARN, + "MergeAttr: Can't inherit from non-existent superclass '%s'", + name); + } + tupleDesc = RelationGetTupleDescriptor(relation); + + for (attrno = relation->rd_rel->relnatts - 1; attrno >= 0; attrno--) { + AttributeTupleForm attribute = tupleDesc->attrs[attrno]; + char *attributeName; + char *attributeType; + HeapTuple tuple; + ColumnDef *def; + TypeName *typename; + + /* + * form name and type + */ + attributeName = (attribute->attname).data; + tuple = + SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(attribute->atttypid), + 0,0,0); + AssertState(HeapTupleIsValid(tuple)); + attributeType = + (((TypeTupleForm)GETSTRUCT(tuple))->typname).data; + /* + * check validity + * + */ + if (checkAttrExists(attributeName, attributeType, inhSchema) || + checkAttrExists(attributeName, attributeType, schema)) { + /* + * this entry already exists + */ + continue; + } + + /* + * add an entry to the schema + */ + def = makeNode(ColumnDef); + typename = makeNode(TypeName); + def->colname = pstrdup(attributeName); + typename->name = pstrdup(attributeType); + def->typename = typename; + partialResult = lcons(def, partialResult); + } + + /* + * iteration cleanup and result collection + */ + heap_close(relation); + + /* + * wants the inherited schema to appear in the order they are + * specified in CREATE TABLE + */ + inhSchema = nconc(inhSchema, partialResult); + } + + /* + * put the inherited schema before our the schema for this table + */ + schema = nconc(inhSchema, schema); + + return (schema); +} + +/* + * StoreCatalogInheritance -- + * Updates the system catalogs with proper inheritance information. + */ +static void +StoreCatalogInheritance(Oid relationId, List *supers) +{ + Relation relation; + TupleDesc desc; + int16 seqNumber; + List *entry; + List *idList; + HeapTuple tuple; + + /* ---------------- + * sanity checks + * ---------------- + */ + AssertArg(OidIsValid(relationId)); + + if (supers==NIL) + return; + + /* ---------------- + * Catalog INHERITS information. + * ---------------- + */ + relation = heap_openr( InheritsRelationName ); + desc = RelationGetTupleDescriptor(relation); + + seqNumber = 1; + idList = NIL; + foreach (entry, supers) { + Datum datum[ Natts_pg_inherits ]; + char nullarr[ Natts_pg_inherits ]; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(strVal(lfirst(entry))), + 0,0,0); + AssertArg(HeapTupleIsValid(tuple)); + + /* + * build idList for use below + */ + idList = lappendi(idList, tuple->t_oid); + + datum[0] = ObjectIdGetDatum(relationId); /* inhrel */ + datum[1] = ObjectIdGetDatum(tuple->t_oid); /* inhparent */ + datum[2] = Int16GetDatum(seqNumber); /* inhseqno */ + + nullarr[0] = ' '; + nullarr[1] = ' '; + nullarr[2] = ' '; + + tuple = heap_formtuple(desc,datum, nullarr); + + (void) heap_insert(relation, tuple); + pfree(tuple); + + seqNumber += 1; + } + + heap_close(relation); + + /* ---------------- + * Catalog IPL information. + * + * Algorithm: + * 0. list superclasses (by Oid) in order given (see idList). + * 1. append after each relationId, its superclasses, recursively. + * 3. remove all but last of duplicates. + * 4. store result. + * ---------------- + */ + + /* ---------------- + * 1. + * ---------------- + */ + foreach (entry, idList) { + HeapTuple tuple; + Oid id; + int16 number; + List *next; + List *current; + + id = (Oid)lfirsti(entry); + current = entry; + next = lnext(entry); + + for (number = 1; ; number += 1) { + tuple = SearchSysCacheTuple(INHRELID, + ObjectIdGetDatum(id), + Int16GetDatum(number), + 0,0); + + if (! HeapTupleIsValid(tuple)) + break; + + lnext(current) = + lconsi(((InheritsTupleForm) + GETSTRUCT(tuple))->inhparent, + NIL); + + current = lnext(current); + } + lnext(current) = next; + } + + /* ---------------- + * 2. + * ---------------- + */ + foreach (entry, idList) { + Oid name; + List *rest; + bool found = false; + + again: + name = lfirsti(entry); + foreach (rest, lnext(entry)) { + if (name == lfirsti(rest)) { + found = true; + break; + } + } + if (found) { + /* + * entry list must be of length >= 2 or else no match + * + * so, remove this entry. + */ + lfirst(entry) = lfirst(lnext(entry)); + lnext(entry) = lnext(lnext(entry)); + + found = false; + goto again; + } + } + + /* ---------------- + * 3. + * ---------------- + */ + relation = heap_openr( InheritancePrecidenceListRelationName ); + desc = RelationGetTupleDescriptor(relation); + + seqNumber = 1; + + foreach (entry, idList) { + Datum datum[ Natts_pg_ipl ]; + char nullarr[ Natts_pg_ipl ]; + + datum[0] = ObjectIdGetDatum(relationId); /* iplrel */ + datum[1] = ObjectIdGetDatum(lfirsti(entry)); + /*iplinherits*/ + datum[2] = Int16GetDatum(seqNumber); /* iplseqno */ + + nullarr[0] = ' '; + nullarr[1] = ' '; + nullarr[2] = ' '; + + tuple = heap_formtuple( desc, datum, nullarr); + + (void) heap_insert(relation, tuple); + pfree(tuple); + + seqNumber += 1; + } + + heap_close(relation); +} + +/* + * returns 1 if attribute already exists in schema, 0 otherwise. + */ +static int +checkAttrExists(char *attributeName, char *attributeType, List *schema) +{ + List *s; + + foreach (s, schema) { + ColumnDef *def = lfirst(s); + + if (!strcmp(attributeName, def->colname)) { + /* + * attribute exists. Make sure the types are the same. + */ + if (strcmp(attributeType, def->typename->name) != 0) { + elog(WARN, "%s and %s conflict for %s", + attributeType, def->typename->name, attributeName); + } + return 1; + } + } + return 0; +} + +/* + * MakeArchiveName + * make an archive rel name out of a regular rel name + * +* the CALLER is responsible for freeing the memory allocated + */ + +char* +MakeArchiveName(Oid relationId) +{ + char *arch; + + /* + * Archive relations are named a,XXXXX where XXXXX == the OID + * of the relation they archive. Create a string containing + * this name and find the reldesc for the archive relation. + */ + arch = palloc(NAMEDATALEN); + sprintf(arch, "a,%d",relationId); + + return arch; +} + |