aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/creatinh.c93
-rw-r--r--src/backend/commands/define.c363
-rw-r--r--src/backend/commands/remove.c59
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.
*