* pg_type.c
* routines to support manipulation of the pg_type relation
*
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.79 2002/08/24 15:00:46 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.104 2005/10/15 02:49:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
-#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "utils/builtins.h"
/*
* open pg_type
*/
- pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
+ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
tupDesc = pg_type_desc->rd_att;
/*
i = 0;
namestrcpy(&name, typeName);
values[i++] = NameGetDatum(&name); /* typname */
- values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
- values[i++] = ObjectIdGetDatum(InvalidOid); /* typowner */
+ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
+ values[i++] = ObjectIdGetDatum(GetUserId()); /* typowner */
values[i++] = Int16GetDatum(0); /* typlen */
values[i++] = BoolGetDatum(false); /* typbyval */
values[i++] = CharGetDatum(0); /* typtype */
values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */
values[i++] = ObjectIdGetDatum(InvalidOid); /* typinput */
values[i++] = ObjectIdGetDatum(InvalidOid); /* typoutput */
- values[i++] = CharGetDatum('i'); /* typalign */
- values[i++] = CharGetDatum('p'); /* typstorage */
- values[i++] = BoolGetDatum(false); /* typnotnull */
- values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
- values[i++] = Int32GetDatum(-1); /* typtypmod */
- values[i++] = Int32GetDatum(0); /* typndims */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */
+ values[i++] = CharGetDatum('i'); /* typalign */
+ values[i++] = CharGetDatum('p'); /* typstorage */
+ values[i++] = BoolGetDatum(false); /* typnotnull */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
+ values[i++] = Int32GetDatum(-1); /* typtypmod */
+ values[i++] = Int32GetDatum(0); /* typndims */
nulls[i++] = 'n'; /* typdefaultbin */
nulls[i++] = 'n'; /* typdefault */
CatalogUpdateIndexes(pg_type_desc, tup);
+ /*
+ * Create dependencies. We can/must skip this in bootstrap mode.
+ */
+ if (!IsBootstrapProcessingMode())
+ GenerateTypeDependencies(typeNamespace,
+ typoid,
+ InvalidOid,
+ 0,
+ GetUserId(),
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ NULL,
+ false);
+
/*
* clean up and return the type-oid
*/
*
* This does all the necessary work needed to define a new type.
*
- * NOTE: if assignedTypeOid is not InvalidOid, then that OID is assigned to
- * the new type (which, therefore, cannot already exist as a shell type).
- * This hack is only intended for use in creating a relation's associated
- * type, where we need to have created the relation tuple already.
+ * Returns the OID assigned to the new type.
* ----------------------------------------------------------------
*/
Oid
TypeCreate(const char *typeName,
Oid typeNamespace,
- Oid assignedTypeOid,
- Oid relationOid, /* only for 'c'atalog typeTypes */
+ Oid relationOid, /* only for 'c'atalog types */
+ char relationKind, /* ditto */
int16 internalSize,
char typeType,
char typDelim,
Oid inputProcedure,
Oid outputProcedure,
+ Oid receiveProcedure,
+ Oid sendProcedure,
+ Oid analyzeProcedure,
Oid elementType,
Oid baseType,
- const char *defaultTypeValue, /* human readable rep */
- const char *defaultTypeBin, /* cooked rep */
+ const char *defaultTypeValue, /* human readable rep */
+ char *defaultTypeBin, /* cooked rep */
bool passedByValue,
char alignment,
char storage,
int32 typeMod,
- int32 typNDims, /* Array dimensions for baseType */
+ int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull)
{
Relation pg_type_desc;
Oid typeObjectId;
+ bool rebuildDeps = false;
HeapTuple tup;
char nulls[Natts_pg_type];
char replaces[Natts_pg_type];
Datum values[Natts_pg_type];
NameData name;
- TupleDesc tupDesc;
int i;
/*
- * We assume that the caller validated the arguments individually,
- * but did not check for bad combinations.
+ * We assume that the caller validated the arguments individually, but did
+ * not check for bad combinations.
*
* Validate size specifications: either positive (fixed-length) or -1
* (varlena) or -2 (cstring). Pass-by-value types must have a fixed
if (!(internalSize > 0 ||
internalSize == -1 ||
internalSize == -2))
- elog(ERROR, "TypeCreate: invalid type internal size %d",
- internalSize);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid type internal size %d",
+ internalSize)));
if (passedByValue &&
(internalSize <= 0 || internalSize > (int16) sizeof(Datum)))
- elog(ERROR, "TypeCreate: invalid type internal size %d",
- internalSize);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("internal size %d is invalid for passed-by-value type",
+ internalSize)));
/* Only varlena types can be toasted */
if (storage != 'p' && internalSize != -1)
- elog(ERROR, "TypeCreate: fixed size types must have storage PLAIN");
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("fixed-size types must have storage PLAIN")));
/*
* initialize arrays needed for heap_formtuple or heap_modifytuple
i = 0;
namestrcpy(&name, typeName);
values[i++] = NameGetDatum(&name); /* typname */
- values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
- values[i++] = Int32GetDatum(GetUserId()); /* typowner */
+ values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
+ values[i++] = ObjectIdGetDatum(GetUserId()); /* typowner */
values[i++] = Int16GetDatum(internalSize); /* typlen */
values[i++] = BoolGetDatum(passedByValue); /* typbyval */
values[i++] = CharGetDatum(typeType); /* typtype */
- values[i++] = BoolGetDatum(true); /* typisdefined */
+ values[i++] = BoolGetDatum(true); /* typisdefined */
values[i++] = CharGetDatum(typDelim); /* typdelim */
values[i++] = ObjectIdGetDatum(typeType == 'c' ? relationOid : InvalidOid); /* typrelid */
- values[i++] = ObjectIdGetDatum(elementType); /* typelem */
- values[i++] = ObjectIdGetDatum(inputProcedure); /* typinput */
- values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */
+ values[i++] = ObjectIdGetDatum(elementType); /* typelem */
+ values[i++] = ObjectIdGetDatum(inputProcedure); /* typinput */
+ values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */
+ values[i++] = ObjectIdGetDatum(receiveProcedure); /* typreceive */
+ values[i++] = ObjectIdGetDatum(sendProcedure); /* typsend */
+ values[i++] = ObjectIdGetDatum(analyzeProcedure); /* typanalyze */
values[i++] = CharGetDatum(alignment); /* typalign */
values[i++] = CharGetDatum(storage); /* typstorage */
- values[i++] = BoolGetDatum(typeNotNull); /* typnotnull */
- values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
- values[i++] = Int32GetDatum(typeMod); /* typtypmod */
- values[i++] = Int32GetDatum(typNDims); /* typndims */
+ values[i++] = BoolGetDatum(typeNotNull); /* typnotnull */
+ values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
+ values[i++] = Int32GetDatum(typeMod); /* typtypmod */
+ values[i++] = Int32GetDatum(typNDims); /* typndims */
/*
- * initialize the default binary value for this type. Check for
- * nulls of course.
+ * initialize the default binary value for this type. Check for nulls of
+ * course.
*/
if (defaultTypeBin)
values[i] = DirectFunctionCall1(textin,
* NOTE: updating will not work correctly in bootstrap mode; but we don't
* expect to be overwriting any shell types in bootstrap mode.
*/
- pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
+ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
tup = SearchSysCacheCopy(TYPENAMENSP,
CStringGetDatum(typeName),
{
/*
* check that the type is not already defined. It may exist as a
- * shell type, however (but only if assignedTypeOid is not given).
+ * shell type, however.
*/
- if (((Form_pg_type) GETSTRUCT(tup))->typisdefined ||
- assignedTypeOid != InvalidOid)
- elog(ERROR, "type %s already exists", typeName);
+ if (((Form_pg_type) GETSTRUCT(tup))->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists", typeName)));
/*
* Okay to update existing "shell" type tuple
*/
tup = heap_modifytuple(tup,
- pg_type_desc,
+ RelationGetDescr(pg_type_desc),
values,
nulls,
replaces);
simple_heap_update(pg_type_desc, &tup->t_self, tup);
- AssertTupleDescHasOid(pg_type_desc->rd_att);
typeObjectId = HeapTupleGetOid(tup);
+
+ rebuildDeps = true; /* get rid of shell type's dependencies */
}
else
{
- tupDesc = pg_type_desc->rd_att;
-
- tup = heap_formtuple(tupDesc,
+ tup = heap_formtuple(RelationGetDescr(pg_type_desc),
values,
nulls);
- /* preassign tuple Oid, if one was given */
- AssertTupleDescHasOid(tupDesc);
- HeapTupleSetOid(tup, assignedTypeOid);
-
typeObjectId = simple_heap_insert(pg_type_desc, tup);
}
* Create dependencies. We can/must skip this in bootstrap mode.
*/
if (!IsBootstrapProcessingMode())
+ GenerateTypeDependencies(typeNamespace,
+ typeObjectId,
+ relationOid,
+ relationKind,
+ GetUserId(),
+ inputProcedure,
+ outputProcedure,
+ receiveProcedure,
+ sendProcedure,
+ analyzeProcedure,
+ elementType,
+ baseType,
+ (defaultTypeBin ?
+ stringToNode(defaultTypeBin) :
+ NULL),
+ rebuildDeps);
+
+ /*
+ * finish up
+ */
+ heap_close(pg_type_desc, RowExclusiveLock);
+
+ return typeObjectId;
+}
+
+/*
+ * GenerateTypeDependencies: build the dependencies needed for a type
+ *
+ * If rebuild is true, we remove existing dependencies and rebuild them
+ * from scratch. This is needed for ALTER TYPE, and also when replacing
+ * a shell type.
+ *
+ * NOTE: a shell type will have a dependency to its namespace, and no others.
+ */
+void
+GenerateTypeDependencies(Oid typeNamespace,
+ Oid typeObjectId,
+ Oid relationOid, /* only for 'c'atalog types */
+ char relationKind, /* ditto */
+ Oid owner,
+ Oid inputProcedure,
+ Oid outputProcedure,
+ Oid receiveProcedure,
+ Oid sendProcedure,
+ Oid analyzeProcedure,
+ Oid elementType,
+ Oid baseType,
+ Node *defaultExpr,
+ bool rebuild)
+{
+ ObjectAddress myself,
+ referenced;
+
+ if (rebuild)
+ {
+ deleteDependencyRecordsFor(TypeRelationId, typeObjectId);
+ deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId);
+ }
+
+ myself.classId = TypeRelationId;
+ myself.objectId = typeObjectId;
+ myself.objectSubId = 0;
+
+ /* dependency on namespace */
+ /* skip for relation rowtype, since we have indirect dependency */
+ if (!OidIsValid(relationOid))
{
- ObjectAddress myself,
- referenced;
-
- myself.classId = RelOid_pg_type;
- myself.objectId = typeObjectId;
- myself.objectSubId = 0;
-
- /* dependency on namespace */
- /* skip for relation rowtype, since we have indirect dependency */
- if (!OidIsValid(relationOid))
- {
- referenced.classId = get_system_catalog_relid(NamespaceRelationName);
- referenced.objectId = typeNamespace;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
-
- /* Normal dependencies on the I/O functions */
- referenced.classId = RelOid_pg_proc;
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = typeNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ /* Normal dependencies on the I/O functions */
+ if (OidIsValid(inputProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
referenced.objectId = inputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- referenced.classId = RelOid_pg_proc;
+ if (OidIsValid(outputProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
referenced.objectId = outputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- /*
- * If the type is a rowtype for a relation, mark it as internally
- * dependent on the relation, *unless* it is a stand-alone composite
- * type relation. For the latter case, we have to reverse the
- * dependency.
- *
- * In the former case, this allows the type to be auto-dropped
- * when the relation is, and not otherwise. And in the latter,
- * of course we get the opposite effect.
- */
- if (OidIsValid(relationOid))
- {
- Relation rel = relation_open(relationOid, AccessShareLock);
- char relkind = rel->rd_rel->relkind;
- relation_close(rel, AccessShareLock);
-
- referenced.classId = RelOid_pg_class;
- referenced.objectId = relationOid;
- referenced.objectSubId = 0;
-
- if (relkind != RELKIND_COMPOSITE_TYPE)
- recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
- else
- recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
- }
+ if (OidIsValid(receiveProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = receiveProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- /*
- * If the type is an array type, mark it auto-dependent on the base
- * type. (This is a compromise between the typical case where the
- * array type is automatically generated and the case where it is
- * manually created: we'd prefer INTERNAL for the former case and
- * NORMAL for the latter.)
- */
- if (OidIsValid(elementType))
- {
- referenced.classId = RelOid_pg_type;
- referenced.objectId = elementType;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
- }
-
- /* Normal dependency from a domain to its base type. */
- if (OidIsValid(baseType))
- {
- referenced.classId = RelOid_pg_type;
- referenced.objectId = baseType;
- referenced.objectSubId = 0;
- recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- }
+ if (OidIsValid(sendProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = sendProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ if (OidIsValid(analyzeProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = analyzeProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/*
- * finish up
+ * If the type is a rowtype for a relation, mark it as internally
+ * dependent on the relation, *unless* it is a stand-alone composite type
+ * relation. For the latter case, we have to reverse the dependency.
+ *
+ * In the former case, this allows the type to be auto-dropped when the
+ * relation is, and not otherwise. And in the latter, of course we get the
+ * opposite effect.
*/
- heap_close(pg_type_desc, RowExclusiveLock);
+ if (OidIsValid(relationOid))
+ {
+ referenced.classId = RelationRelationId;
+ referenced.objectId = relationOid;
+ referenced.objectSubId = 0;
- return typeObjectId;
+ if (relationKind != RELKIND_COMPOSITE_TYPE)
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ else
+ recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
+ }
+
+ /*
+ * If the type is an array type, mark it auto-dependent on the base type.
+ * (This is a compromise between the typical case where the array type is
+ * automatically generated and the case where it is manually created: we'd
+ * prefer INTERNAL for the former case and NORMAL for the latter.)
+ */
+ if (OidIsValid(elementType))
+ {
+ referenced.classId = TypeRelationId;
+ referenced.objectId = elementType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+ }
+
+ /* Normal dependency from a domain to its base type. */
+ if (OidIsValid(baseType))
+ {
+ referenced.classId = TypeRelationId;
+ referenced.objectId = baseType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ /* Normal dependency on the default expression. */
+ if (defaultExpr)
+ recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ /* Shared dependency on owner. */
+ recordDependencyOnOwner(TypeRelationId, typeObjectId, owner);
}
/*
Relation pg_type_desc;
HeapTuple tuple;
- pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
+ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
tuple = SearchSysCacheCopy(TYPENAMENSP,
CStringGetDatum(oldTypeName),
ObjectIdGetDatum(typeNamespace),
0, 0);
if (!HeapTupleIsValid(tuple))
- elog(ERROR, "type %s does not exist", oldTypeName);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" does not exist", oldTypeName)));
if (SearchSysCacheExists(TYPENAMENSP,
CStringGetDatum(newTypeName),
ObjectIdGetDatum(typeNamespace),
0, 0))
- elog(ERROR, "type named %s already exists", newTypeName);
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists", newTypeName)));
namestrcpy(&(((Form_pg_type) GETSTRUCT(tuple))->typname), newTypeName);