diff options
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/creatinh.c | 93 | ||||
-rw-r--r-- | src/backend/commands/define.c | 363 | ||||
-rw-r--r-- | src/backend/commands/remove.c | 59 |
3 files changed, 507 insertions, 8 deletions
diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c index 27535429c39..586cee63cce 100644 --- a/src/backend/commands/creatinh.c +++ b/src/backend/commands/creatinh.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.83 2002/03/06 06:09:31 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.84 2002/03/06 20:34:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -39,7 +39,7 @@ static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno); static void StoreCatalogInheritance(Oid relationId, List *supers); static int findAttrByName(const char *attributeName, List *schema); static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass); - +static List *MergeDomainAttributes(List *schema); /* ---------------------------------------------------------------- * DefineRelation @@ -70,6 +70,13 @@ DefineRelation(CreateStmt *stmt, char relkind) StrNCpy(relname, stmt->relname, NAMEDATALEN); /* + * Merge domain attributes into the known columns before inheritance + * applies it's changes otherwise we risk adding double constraints + * to a domain thats inherited. + */ + schema = MergeDomainAttributes(schema); + + /* * Look up inheritance ancestors and generate relation schema, * including inherited attributes. */ @@ -237,6 +244,88 @@ TruncateRelation(char *name) heap_truncate(name); } + +/* + * MergeDomainAttributes + * Returns a new schema with the constraints, types, and other + * attributes of the domain resolved. + * + * Defaults are processed at execution time by taking the default of + * the type (domain) if it is null. This does not need to be merged + * here. + */ +static List * +MergeDomainAttributes(List *schema) +{ + List *entry; + + /* + * Loop through the table elements supplied. These should + * never include inherited domains else they'll be + * double (or more) processed. + */ + foreach(entry, schema) + { + ColumnDef *coldef = lfirst(entry); + HeapTuple tuple; + Form_pg_type typeTup; + + + tuple = SearchSysCache(TYPENAME, + CStringGetDatum(coldef->typename->name), + 0,0,0); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "MergeDomainAttributes: Type %s does not exist", + coldef->typename->name); + + typeTup = (Form_pg_type) GETSTRUCT(tuple); + if (typeTup->typtype == 'd') { + /* + * This is a domain, lets force the properties of the domain on to + * the new column. + */ + + /* Enforce the typmod value */ + coldef->typename->typmod = typeTup->typmod; + + /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */ + coldef->is_not_null |= typeTup->typnotnull; + + /* Enforce the element type in the event the domain is an array + * + * BUG: How do we fill out arrayBounds and attrname from typelem and typNDimms? + */ + + } + ReleaseSysCache(tuple); + +//typedef struct TypeName +//{ + //NodeTag type; + //char *name; /* name of the type */ + //bool timezone; /* timezone specified? */ + //bool setof; /* is a set? */ + //int32 typmod; /* type modifier */ + //List *arrayBounds; /* array bounds */ + //char *attrname; /* field name when using %TYPE */ +//} TypeName; + +// ColumnDef +// NodeTag type; +// char *colname; /* name of column */ +// TypeName *typename; /* type of column */ +// bool is_not_null; /* NOT NULL constraint specified? */ +// Node *raw_default; /* default value (untransformed parse +// * tree) */ +// char *cooked_default; /* nodeToString representation */ +// List *constraints; /* other constraints on column */ + + } + + return schema; +} + /*---------- * MergeAttributes * Returns new schema given initial schema and superclasses. diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 841fc73c309..cb71340eb2c 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.67 2002/03/06 06:09:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.68 2002/03/06 20:34:47 momjian Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -40,6 +40,7 @@ #include "access/heapam.h" #include "catalog/catname.h" +#include "catalog/heap.h" #include "catalog/pg_aggregate.h" #include "catalog/pg_language.h" #include "catalog/pg_operator.h" @@ -476,6 +477,322 @@ DefineAggregate(char *aggName, List *parameters) } /* + * DefineDomain + * Registers a new domain. + */ +void +DefineDomain(CreateDomainStmt *stmt) +{ + int16 internalLength = -1; /* int2 */ + int16 externalLength = -1; /* int2 */ + char *inputName = NULL; + char *outputName = NULL; + char *sendName = NULL; + char *receiveName = NULL; + + /* + * Domains store the external representation in defaultValue + * and the interal Node representation in defaultValueBin + */ + char *defaultValue = NULL; + char *defaultValueBin = NULL; + + bool byValue = false; + char delimiter = DEFAULT_TYPDELIM; + char alignment = 'i'; /* default alignment */ + char storage = 'p'; /* default TOAST storage method */ + char typtype; + Datum datum; + bool typNotNull = false; + char *elemName = NULL; + int32 typNDims = 0; /* No array dimensions by default */ + + bool isnull; + Relation pg_type_rel; + TupleDesc pg_type_dsc; + HeapTuple typeTup; + char *typeName = stmt->typename->name; + + List *listptr; + List *schema = stmt->constraints; + + /* + * Domainnames, unlike typenames don't need to account for the '_' + * prefix. So they can be one character longer. + */ + if (strlen(stmt->domainname) > (NAMEDATALEN - 1)) + elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less", + NAMEDATALEN - 1); + + + /* Test for existing Domain (or type) of that name */ + typeTup = SearchSysCache( TYPENAME + , PointerGetDatum(stmt->domainname) + , 0, 0, 0 + ); + + if (HeapTupleIsValid(typeTup)) + { + elog(ERROR, "CREATE DOMAIN: domain or type %s already exists", + stmt->domainname); + } + + /* + * Get the information about old types + */ + pg_type_rel = heap_openr(TypeRelationName, RowExclusiveLock); + pg_type_dsc = RelationGetDescr(pg_type_rel); + + + /* + * When the type is an array for some reason we don't actually receive + * the name here. We receive the base types name. Lets set Dims while + * were at it. + */ + if (stmt->typename->arrayBounds > 0) { + typeName = makeArrayTypeName(stmt->typename->name); + + typNDims = length(stmt->typename->arrayBounds); + } + + + typeTup = SearchSysCache( TYPENAME + , PointerGetDatum(typeName) + , 0, 0, 0 + ); + + if (!HeapTupleIsValid(typeTup)) + { + elog(ERROR, "CREATE DOMAIN: type %s does not exist", + stmt->typename->name); + } + + + /* Check that this is a basetype */ + typtype = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typtype, pg_type_dsc, &isnull)); + Assert(!isnull); + + /* + * What we really don't want is domains of domains. This could cause all sorts + * of neat issues if we allow that. + * + * With testing, we may determine complex types should be allowed + */ + if (typtype != 'b') { + elog(ERROR, "DefineDomain: %s is not a basetype", stmt->typename->name); + } + + /* passed by value */ + byValue = DatumGetBool(heap_getattr(typeTup, Anum_pg_type_typbyval, pg_type_dsc, &isnull)); + Assert(!isnull); + + /* Required Alignment */ + alignment = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typalign, pg_type_dsc, &isnull)); + Assert(!isnull); + + /* Storage Length */ + internalLength = DatumGetInt16(heap_getattr(typeTup, Anum_pg_type_typlen, pg_type_dsc, &isnull)); + Assert(!isnull); + + /* External Length (unused) */ + externalLength = DatumGetInt16(heap_getattr(typeTup, Anum_pg_type_typprtlen, pg_type_dsc, &isnull)); + Assert(!isnull); + + /* Array element Delimiter */ + delimiter = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typdelim, pg_type_dsc, &isnull)); + Assert(!isnull); + + /* Input Function Name */ + datum = heap_getattr(typeTup, Anum_pg_type_typinput, pg_type_dsc, &isnull); + Assert(!isnull); + + inputName = DatumGetCString(DirectFunctionCall1(regprocout, datum)); + + /* Output Function Name */ + datum = heap_getattr(typeTup, Anum_pg_type_typoutput, pg_type_dsc, &isnull); + Assert(!isnull); + + outputName = DatumGetCString(DirectFunctionCall1(regprocout, datum)); + + /* ReceiveName */ + datum = heap_getattr(typeTup, Anum_pg_type_typreceive, pg_type_dsc, &isnull); + Assert(!isnull); + + receiveName = DatumGetCString(DirectFunctionCall1(regprocout, datum)); + + /* SendName */ + datum = heap_getattr(typeTup, Anum_pg_type_typsend, pg_type_dsc, &isnull); + Assert(!isnull); + + sendName = DatumGetCString(DirectFunctionCall1(regprocout, datum)); + + /* TOAST Strategy */ + storage = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typstorage, pg_type_dsc, &isnull)); + Assert(!isnull); + + /* Inherited default value */ + datum = heap_getattr(typeTup, Anum_pg_type_typdefault, pg_type_dsc, &isnull); + if (!isnull) { + defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum)); + } + + /* + * Pull out the typelem name of the parent OID. + * + * This is what enables us to make a domain of an array + */ + datum = heap_getattr(typeTup, Anum_pg_type_typelem, pg_type_dsc, &isnull); + Assert(!isnull); + + if (DatumGetObjectId(datum) != InvalidOid) { + HeapTuple tup; + + tup = SearchSysCache( TYPEOID + , datum + , 0, 0, 0 + ); + + elemName = NameStr(((Form_pg_type) GETSTRUCT(tup))->typname); + + ReleaseSysCache(tup); + } + + + /* + * Run through constraints manually avoids the additional + * processing conducted by DefineRelation() and friends. + * + * Besides, we don't want any constraints to be cooked. We'll + * do that when the table is created via MergeDomainAttributes(). + */ + foreach(listptr, schema) + { + bool nullDefined = false; + Node *expr; + Constraint *colDef = lfirst(listptr); + + /* Used for the statement transformation */ + ParseState *pstate; + + /* + * Create a dummy ParseState and insert the target relation as its + * sole rangetable entry. We need a ParseState for transformExpr. + */ + pstate = make_parsestate(NULL); + + switch(colDef->contype) { + /* + * The inherited default value may be overridden by the user + * with the DEFAULT <expr> statement. + * + * We have to search the entire constraint tree returned as we + * don't want to cook or fiddle too much. + */ + case CONSTR_DEFAULT: + + /* + * Cook the colDef->raw_expr into an expression to ensure + * that it can be done. We store the text version of the + * raw value. + * + * Note: Name is strictly for error message + */ + expr = cookDefault(pstate, colDef->raw_expr + , typeTup->t_data->t_oid + , stmt->typename->typmod + , stmt->typename->name); + + /* Binary default required */ + defaultValue = deparse_expression(expr, + deparse_context_for(stmt->domainname, + InvalidOid), + false); + + defaultValueBin = nodeToString(expr); + + break; + + /* + * Find the NULL constraint. + */ + case CONSTR_NOTNULL: + if (nullDefined) { + elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); + } else { + typNotNull = true; + nullDefined = true; + } + + break; + + case CONSTR_NULL: + if (nullDefined) { + elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); + } else { + typNotNull = false; + nullDefined = true; + } + + break; + + case CONSTR_UNIQUE: + elog(ERROR, "CREATE DOMAIN / UNIQUE indecies not supported"); + break; + + case CONSTR_PRIMARY: + elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indecies not supported"); + break; + + + case CONSTR_CHECK: + + elog(ERROR, "defineDomain: CHECK Constraints not supported"); + break; + + case CONSTR_ATTR_DEFERRABLE: + case CONSTR_ATTR_NOT_DEFERRABLE: + case CONSTR_ATTR_DEFERRED: + case CONSTR_ATTR_IMMEDIATE: + elog(ERROR, "defineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported"); + break; + } + + } + + /* + * Have TypeCreate do all the real work. + */ + TypeCreate(stmt->domainname, /* type name */ + InvalidOid, /* preassigned type oid (not done here) */ + InvalidOid, /* relation oid (n/a here) */ + internalLength, /* internal size */ + externalLength, /* external size */ + 'd', /* type-type (domain type) */ + delimiter, /* array element delimiter */ + inputName, /* input procedure */ + outputName, /* output procedure */ + receiveName, /* receive procedure */ + sendName, /* send procedure */ + elemName, /* element type name */ + typeName, /* base type name */ + defaultValue, /* default type value */ + defaultValueBin, /* default type value */ + byValue, /* passed by value */ + alignment, /* required alignment */ + storage, /* TOAST strategy */ + stmt->typename->typmod, /* typeMod value */ + typNDims, /* Array dimensions for base type */ + typNotNull); /* Type NOT NULL */ + + /* + * Now we can clean up. + */ + ReleaseSysCache(typeTup); + heap_close(pg_type_rel, NoLock); +} + + +/* * DefineType * Registers a new type. */ @@ -490,6 +807,8 @@ DefineType(char *typeName, List *parameters) char *sendName = NULL; char *receiveName = NULL; char *defaultValue = NULL; + char *defaultValueBin = NULL; + Node *defaultRaw = (Node *) NULL; bool byValue = false; char delimiter = DEFAULT_TYPDELIM; char *shadow_type; @@ -531,7 +850,7 @@ DefineType(char *typeName, List *parameters) else if (strcasecmp(defel->defname, "element") == 0) elemName = defGetString(defel); else if (strcasecmp(defel->defname, "default") == 0) - defaultValue = defGetString(defel); + defaultRaw = defel->arg; else if (strcasecmp(defel->defname, "passedbyvalue") == 0) byValue = true; else if (strcasecmp(defel->defname, "alignment") == 0) @@ -591,6 +910,32 @@ DefineType(char *typeName, List *parameters) if (outputName == NULL) elog(ERROR, "Define: \"output\" unspecified"); + + if (defaultRaw) { + Node *expr; + ParseState *pstate; + + /* + * Create a dummy ParseState and insert the target relation as its + * sole rangetable entry. We need a ParseState for transformExpr. + */ + pstate = make_parsestate(NULL); + + expr = cookDefault(pstate, defaultRaw, + InvalidOid, + -1, + typeName); + + /* Binary default required */ + defaultValue = deparse_expression(expr, + deparse_context_for(typeName, + InvalidOid), + false); + + defaultValueBin = nodeToString(expr); + } + + /* * now have TypeCreate do all the real work. */ @@ -606,10 +951,15 @@ DefineType(char *typeName, List *parameters) receiveName, /* receive procedure */ sendName, /* send procedure */ elemName, /* element type name */ + NULL, /* base type name (Non-zero for domains) */ defaultValue, /* default type value */ + defaultValueBin, /* default type value (Binary form) */ byValue, /* passed by value */ alignment, /* required alignment */ - storage); /* TOAST strategy */ + storage, /* TOAST strategy */ + -1, /* typMod (Domains only) */ + 0, /* Array Dimensions of typbasetype */ + 'f'); /* Type NOT NULL */ /* * When we create a base type (as opposed to a complex type) we need @@ -632,10 +982,15 @@ DefineType(char *typeName, List *parameters) "array_in", /* receive procedure */ "array_out", /* send procedure */ typeName, /* element type name */ + NULL, /* base type name */ NULL, /* never a default type value */ + NULL, /* binary default isn't sent either */ false, /* never passed by value */ alignment, /* see above */ - 'x'); /* ARRAY is always toastable */ + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + 'f'); /* Type NOT NULL */ pfree(shadow_type); } diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c index a0456adeb93..69e6e1b9000 100644 --- a/src/backend/commands/remove.c +++ b/src/backend/commands/remove.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * remove.c - * POSTGRES remove (function | type | operator ) utilty code. + * POSTGRES remove (domain | function | type | operator ) utilty code. * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.66 2002/03/06 06:09:35 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.67 2002/03/06 20:34:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "commands/comment.h" #include "commands/defrem.h" #include "miscadmin.h" +#include "parser/parse.h" #include "parser/parse_agg.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" @@ -276,6 +277,60 @@ RemoveType(char *typeName) /* type name to be removed */ } /* + * RemoveDomain + * Removes the domain 'typeName' and all attributes and relations that + * use it. + */ +void +RemoveDomain(char *domainName, int behavior) /* domain name to be removed */ +{ + Relation relation; + HeapTuple tup; + TupleDesc description; + char typtype; + bool isnull; + + + /* Domains are stored as types. Check for permissions on the type */ + if (!pg_ownercheck(GetUserId(), domainName, TYPENAME)) + elog(ERROR, "RemoveDomain: type '%s': permission denied", + domainName); + + + relation = heap_openr(TypeRelationName, RowExclusiveLock); + description = RelationGetDescr(relation); + + tup = SearchSysCache(TYPENAME, + PointerGetDatum(domainName), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "RemoveType: type '%s' does not exist", domainName); + + + /* Check that this is actually a domain */ + typtype = DatumGetChar(heap_getattr(tup, Anum_pg_type_typtype, description, &isnull)); + Assert(!isnull); + + if (typtype != 'd') { + elog(ERROR, "%s is not a domain", domainName); + } + + /* CASCADE unsupported */ + if (behavior == CASCADE) { + elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword"); + } + + /* Delete any comments associated with this type */ + DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation)); + + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(relation, RowExclusiveLock); +} + +/* * RemoveFunction * Deletes a function. * |