/*-------------------------------------------------------------------------
*
- * pg_type.c--
+ * pg_type.c
* routines to support manipulation of the pg_type relation
*
- * Copyright (c) 1994, Regents of the University of California
+ * 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.10 1997/09/08 21:42:24 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.104 2005/10/15 02:49:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
-#include <postgres.h>
-
-#include <utils/syscache.h>
-#include <catalog/pg_proc.h>
-#include <access/heapam.h>
-#include <access/relscan.h>
-#include <utils/builtins.h>
-#include <fmgr.h>
-#include <parser/catalog_utils.h>
-#include <catalog/catname.h>
-#include <catalog/indexing.h>
-#include <storage/lmgr.h>
-#include <miscadmin.h>
-#ifndef HAVE_MEMMOVE
-#include <regex/utils.h>
-#else
-#include <string.h>
-#endif
-
-static Oid
-TypeShellMakeWithOpenRelation(Relation pg_type_desc,
- char *typeName);
+#include "postgres.h"
-/* ----------------------------------------------------------------
- * TypeGetWithOpenRelation
- *
- * preforms a scan on pg_type for a type tuple with the
- * given type name.
- * ----------------------------------------------------------------
- * pg_type_desc -- reldesc for pg_type
- * typeName -- name of type to be fetched
- * defined -- has the type been defined?
- */
-static Oid
-TypeGetWithOpenRelation(Relation pg_type_desc,
- char *typeName,
- bool *defined)
-{
- HeapScanDesc scan;
- HeapTuple tup;
+#include "access/heapam.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"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
- static ScanKeyData typeKey[1] = {
- {0, Anum_pg_type_typname, NameEqualRegProcedure}
- };
-
- /* ----------------
- * initialize the scan key and begin a scan of pg_type
- * ----------------
- */
- fmgr_info(NameEqualRegProcedure,
- &typeKey[0].sk_func, &typeKey[0].sk_nargs);
- typeKey[0].sk_argument = PointerGetDatum(typeName);
-
- scan = heap_beginscan(pg_type_desc,
- 0,
- SelfTimeQual,
- 1,
- typeKey);
-
- /* ----------------
- * get the type tuple, if it exists.
- * ----------------
- */
- tup = heap_getnext(scan, 0, (Buffer *) 0);
-
- /* ----------------
- * if no type tuple exists for the given type name, then
- * end the scan and return appropriate information.
- * ----------------
- */
- if (!HeapTupleIsValid(tup))
- {
- heap_endscan(scan);
- *defined = false;
- return InvalidOid;
- }
-
- /* ----------------
- * here, the type tuple does exist so we pull information from
- * the typisdefined field of the tuple and return the tuple's
- * oid, which is the oid of the type.
- * ----------------
- */
- heap_endscan(scan);
- *defined = (bool) ((TypeTupleForm) GETSTRUCT(tup))->typisdefined;
-
- return
- tup->t_oid;
-}
/* ----------------------------------------------------------------
- * TypeGet
- *
- * Finds the ObjectId of a type, even if uncommitted; "defined"
- * is only set if the type has actually been defined, i.e., if
- * the type tuple is not a shell.
+ * TypeShellMake
*
- * Note: the meat of this function is now in the function
- * TypeGetWithOpenRelation(). -cim 6/15/90
+ * This procedure inserts a "shell" tuple into the type
+ * relation. The type tuple inserted has invalid values
+ * and in particular, the "typisdefined" field is false.
*
- * Also called from util/remove.c
+ * This is used so that a tuple exists in the catalogs.
+ * The invalid fields should be fixed up sometime after
+ * this routine is called, and then the "typeisdefined"
+ * field is set to true. -cim 6/15/90
* ----------------------------------------------------------------
*/
Oid
-TypeGet(char *typeName, /* name of type to be fetched */
- bool *defined) /* has the type been defined? */
+TypeShellMake(const char *typeName, Oid typeNamespace)
{
Relation pg_type_desc;
- Oid typeoid;
-
- /* ----------------
- * open the pg_type relation
- * ----------------
- */
- pg_type_desc = heap_openr(TypeRelationName);
-
- /* ----------------
- * scan the type relation for the information we want
- * ----------------
- */
- typeoid = TypeGetWithOpenRelation(pg_type_desc,
- typeName,
- defined);
-
- /* ----------------
- * close the type relation and return the type oid.
- * ----------------
- */
- heap_close(pg_type_desc);
-
- return
- typeoid;
-}
-
-/* ----------------------------------------------------------------
- * TypeShellMakeWithOpenRelation
- *
- * ----------------------------------------------------------------
- */
-static Oid
-TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName)
-{
- register int i;
+ TupleDesc tupDesc;
+ int i;
HeapTuple tup;
Datum values[Natts_pg_type];
char nulls[Natts_pg_type];
Oid typoid;
- TupleDesc tupDesc;
+ NameData name;
+
+ Assert(PointerIsValid(typeName));
+
+ /*
+ * open pg_type
+ */
+ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
+ tupDesc = pg_type_desc->rd_att;
- /* ----------------
- * initialize our nulls[] and values[] arrays
- * ----------------
+ /*
+ * initialize our *nulls and *values arrays
*/
for (i = 0; i < Natts_pg_type; ++i)
{
values[i] = (Datum) NULL; /* redundant, but safe */
}
- /* ----------------
- * initialize values[] with the type name and
- * ----------------
+ /*
+ * initialize *values with the type name and dummy values
*/
i = 0;
- values[i++] = (Datum) typeName; /* 1 */
- values[i++] = (Datum) InvalidOid; /* 2 */
- values[i++] = (Datum) (int16) 0; /* 3 */
- values[i++] = (Datum) (int16) 0; /* 4 */
- values[i++] = (Datum) (bool) 0; /* 5 */
- values[i++] = (Datum) (bool) 0; /* 6 */
- values[i++] = (Datum) (bool) 0; /* 7 */
- values[i++] = (Datum) (bool) 0; /* 8 */
- values[i++] = (Datum) InvalidOid; /* 9 */
- values[i++] = (Datum) InvalidOid; /* 10 */
- values[i++] = (Datum) InvalidOid; /* 11 */
- values[i++] = (Datum) InvalidOid; /* 12 */
- values[i++] = (Datum) InvalidOid; /* 13 */
- values[i++] = (Datum) InvalidOid; /* 14 */
- values[i++] = (Datum) 'i'; /* 15 */
+ namestrcpy(&name, typeName);
+ values[i++] = NameGetDatum(&name); /* typname */
+ 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++] = BoolGetDatum(false); /* typisdefined */
+ values[i++] = CharGetDatum(0); /* typdelim */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typinput */
+ values[i++] = ObjectIdGetDatum(InvalidOid); /* typoutput */
+ 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 */
/*
- * ... and fill typdefault with a bogus value
- */
- values[i++] =
- (Datum) fmgr(TextInRegProcedure, typeName); /* 15 */
-
- /* ----------------
- * create a new type tuple with FormHeapTuple
- * ----------------
+ * create a new type tuple
*/
- tupDesc = pg_type_desc->rd_att;
-
tup = heap_formtuple(tupDesc, values, nulls);
- /* ----------------
- * insert the tuple in the relation and get the tuple's oid.
- * ----------------
- */
- heap_insert(pg_type_desc, tup);
- typoid = tup->t_oid;
-
- if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex)
- {
- Relation idescs[Num_pg_type_indices];
-
- CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
- CatalogCloseIndices(Num_pg_type_indices, idescs);
- }
- /* ----------------
- * free the tuple and return the type-oid
- * ----------------
+ /*
+ * insert the tuple in the relation and get the tuple's oid.
*/
- pfree(tup);
-
- return
- typoid;
-}
-
-/* ----------------------------------------------------------------
- * TypeShellMake
- *
- * This procedure inserts a "shell" tuple into the type
- * relation. The type tuple inserted has invalid values
- * and in particular, the "typisdefined" field is false.
- *
- * This is used so that a tuple exists in the catalogs.
- * The invalid fields should be fixed up sometime after
- * this routine is called, and then the "typeisdefined"
- * field is set to true. -cim 6/15/90
- * ----------------------------------------------------------------
- */
-Oid
-TypeShellMake(char *typeName)
-{
- Relation pg_type_desc;
- Oid typoid;
+ typoid = simple_heap_insert(pg_type_desc, tup);
- Assert(PointerIsValid(typeName));
+ CatalogUpdateIndexes(pg_type_desc, tup);
- /* ----------------
- * open pg_type
- * ----------------
- */
- pg_type_desc = heap_openr(TypeRelationName);
-
- /* ----------------
- * insert the shell tuple
- * ----------------
+ /*
+ * Create dependencies. We can/must skip this in bootstrap mode.
*/
- typoid = TypeShellMakeWithOpenRelation(pg_type_desc, typeName);
+ if (!IsBootstrapProcessingMode())
+ GenerateTypeDependencies(typeNamespace,
+ typoid,
+ InvalidOid,
+ 0,
+ GetUserId(),
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ InvalidOid,
+ NULL,
+ false);
- /* ----------------
- * close pg_type and return the tuple's oid.
- * ----------------
+ /*
+ * clean up and return the type-oid
*/
- heap_close(pg_type_desc);
+ heap_freetuple(tup);
+ heap_close(pg_type_desc, RowExclusiveLock);
- return
- typoid;
+ return typoid;
}
/* ----------------------------------------------------------------
* TypeCreate
*
* This does all the necessary work needed to define a new type.
+ *
+ * Returns the OID assigned to the new type.
* ----------------------------------------------------------------
*/
Oid
-TypeCreate(char *typeName,
- Oid relationOid, /* only for 'c'atalog typeTypes */
+TypeCreate(const char *typeName,
+ Oid typeNamespace,
+ Oid relationOid, /* only for 'c'atalog types */
+ char relationKind, /* ditto */
int16 internalSize,
- int16 externalSize,
char typeType,
char typDelim,
- char *inputProcedure,
- char *outputProcedure,
- char *sendProcedure,
- char *receiveProcedure,
- char *elementTypeName,
- char *defaultTypeValue, /* internal rep */
+ Oid inputProcedure,
+ Oid outputProcedure,
+ Oid receiveProcedure,
+ Oid sendProcedure,
+ Oid analyzeProcedure,
+ Oid elementType,
+ Oid baseType,
+ const char *defaultTypeValue, /* human readable rep */
+ char *defaultTypeBin, /* cooked rep */
bool passedByValue,
- char alignment)
+ char alignment,
+ char storage,
+ int32 typeMod,
+ int32 typNDims, /* Array dimensions for baseType */
+ bool typeNotNull)
{
- register i,
- j;
Relation pg_type_desc;
- HeapScanDesc pg_type_scan;
-
Oid typeObjectId;
- Oid elementObjectId = InvalidOid;
-
+ bool rebuildDeps = false;
HeapTuple tup;
char nulls[Natts_pg_type];
char replaces[Natts_pg_type];
Datum values[Natts_pg_type];
+ NameData name;
+ int i;
- Buffer buffer;
- char *procname;
- char *procs[4];
- bool defined;
- ItemPointerData itemPointerData;
- TupleDesc tupDesc;
-
- Oid argList[8];
-
-
- static ScanKeyData typeKey[1] = {
- {0, Anum_pg_type_typname, NameEqualRegProcedure}
- };
-
- fmgr_info(NameEqualRegProcedure,
- &typeKey[0].sk_func, &typeKey[0].sk_nargs);
-
- /* ----------------
- * check that the type is not already defined.
- * ----------------
- */
- typeObjectId = TypeGet(typeName, &defined);
- if (OidIsValid(typeObjectId) && defined)
- {
- elog(WARN, "TypeCreate: type %s already defined", typeName);
- }
-
- /* ----------------
- * if this type has an associated elementType, then we check that
- * it is defined.
- * ----------------
+ /*
+ * 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
+ * length not more than sizeof(Datum).
*/
- if (elementTypeName)
- {
- elementObjectId = TypeGet(elementTypeName, &defined);
- if (!defined)
- {
- elog(WARN, "TypeCreate: type %s is not defined", elementTypeName);
- }
- }
+ if (!(internalSize > 0 ||
+ internalSize == -1 ||
+ internalSize == -2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid type internal size %d",
+ internalSize)));
+ if (passedByValue &&
+ (internalSize <= 0 || internalSize > (int16) sizeof(Datum)))
+ 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)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("fixed-size types must have storage PLAIN")));
- /* ----------------
- * XXX comment me
- * ----------------
- */
- if (externalSize == 0)
- {
- externalSize = -1; /* variable length */
- }
-
- /* ----------------
- * initialize arrays needed by FormHeapTuple
- * ----------------
+ /*
+ * initialize arrays needed for heap_formtuple or heap_modifytuple
*/
for (i = 0; i < Natts_pg_type; ++i)
{
nulls[i] = ' ';
replaces[i] = 'r';
- values[i] = (Datum) NULL; /* redundant, but nice */
+ values[i] = (Datum) 0;
}
/*
- * XXX
- *
- * Do this so that user-defined types have size -1 instead of zero if
- * they are variable-length - this is so that everything else in the
- * backend works.
- */
-
- if (internalSize == 0)
- internalSize = -1;
-
- /* ----------------
- * initialize the values[] information
- * ----------------
+ * initialize the *values information
*/
i = 0;
- values[i++] = PointerGetDatum(typeName); /* 1 */
- values[i++] = (Datum) GetUserId(); /* 2 */
- values[i++] = (Datum) internalSize; /* 3 */
- values[i++] = (Datum) externalSize; /* 4 */
- values[i++] = (Datum) passedByValue; /* 5 */
- values[i++] = (Datum) typeType; /* 6 */
- values[i++] = (Datum) (bool) 1; /* 7 */
- values[i++] = (Datum) typDelim; /* 8 */
- values[i++] = (Datum) (typeType == 'c' ? relationOid : InvalidOid); /* 9 */
- values[i++] = (Datum) elementObjectId; /* 10 */
+ namestrcpy(&name, typeName);
+ values[i++] = NameGetDatum(&name); /* typname */
+ 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++] = 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(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 */
/*
- * arguments to type input and output functions must be 0
+ * initialize the default binary value for this type. Check for nulls of
+ * course.
*/
- memset(argList, 0, 8 * sizeof(Oid));
-
- procs[0] = inputProcedure;
- procs[1] = outputProcedure;
- procs[2] = (receiveProcedure) ? receiveProcedure : inputProcedure;
- procs[3] = (sendProcedure) ? sendProcedure : outputProcedure;
-
- for (j = 0; j < 4; ++j)
- {
- procname = procs[j];
-
- tup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(procname),
- Int32GetDatum(1),
- PointerGetDatum(argList),
- 0);
-
- if (!HeapTupleIsValid(tup))
- {
-
- /*
- * it is possible for the input/output procedure to take two
- * arguments, where the second argument is the element type
- * (eg array_in/array_out)
- */
- if (OidIsValid(elementObjectId))
- {
- tup = SearchSysCacheTuple(PRONAME,
- PointerGetDatum(procname),
- Int32GetDatum(2),
- PointerGetDatum(argList),
- 0);
- }
- if (!HeapTupleIsValid(tup))
- {
- func_error("TypeCreate", procname, 1, argList);
- }
- }
-
- values[i++] = (Datum) tup->t_oid; /* 11 - 14 */
- }
+ if (defaultTypeBin)
+ values[i] = DirectFunctionCall1(textin,
+ CStringGetDatum(defaultTypeBin));
+ else
+ nulls[i] = 'n';
+ i++; /* typdefaultbin */
- /* ----------------
- * set default alignment
- * ----------------
+ /*
+ * initialize the default value for this type.
*/
- values[i++] = (Datum) alignment; /* 15 */
+ if (defaultTypeValue)
+ values[i] = DirectFunctionCall1(textin,
+ CStringGetDatum(defaultTypeValue));
+ else
+ nulls[i] = 'n';
+ i++; /* typdefault */
- /* ----------------
- * initialize the default value for this type.
- * ----------------
- */
- values[i] = (Datum) fmgr(TextInRegProcedure, /* 16 */
- PointerIsValid(defaultTypeValue)
- ? defaultTypeValue : "-"); /* XXX default
- * typdefault */
-
- /* ----------------
- * open pg_type and begin a scan for the type name.
- * ----------------
+ /*
+ * open pg_type and prepare to insert or update a row.
+ *
+ * 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);
+ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
- /* -----------------
- * Set a write lock initially so as not upgrade a read to a write
- * when the heap_insert() or heap_replace() is called.
- * -----------------
- */
- RelationSetLockForWrite(pg_type_desc);
-
- typeKey[0].sk_argument = PointerGetDatum(typeName);
- pg_type_scan = heap_beginscan(pg_type_desc,
- 0,
- SelfTimeQual,
- 1,
- typeKey);
-
- /* ----------------
- * define the type either by adding a tuple to the type
- * relation, or by updating the fields of the "shell" tuple
- * already there.
- * ----------------
- */
- tup = heap_getnext(pg_type_scan, 0, &buffer);
+ tup = SearchSysCacheCopy(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace),
+ 0, 0);
if (HeapTupleIsValid(tup))
{
+ /*
+ * check that the type is not already defined. It may exist as a
+ * shell type, however.
+ */
+ 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,
- buffer,
- pg_type_desc,
+ RelationGetDescr(pg_type_desc),
values,
nulls,
replaces);
- /* XXX may not be necessary */
- ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ simple_heap_update(pg_type_desc, &tup->t_self, tup);
- setheapoverride(true);
- heap_replace(pg_type_desc, &itemPointerData, tup);
- setheapoverride(false);
+ typeObjectId = HeapTupleGetOid(tup);
- typeObjectId = tup->t_oid;
+ 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);
- heap_insert(pg_type_desc, tup);
-
- typeObjectId = tup->t_oid;
+ typeObjectId = simple_heap_insert(pg_type_desc, tup);
}
- /* ----------------
- * finish up
- * ----------------
- */
- heap_endscan(pg_type_scan);
+ /* Update indexes */
+ CatalogUpdateIndexes(pg_type_desc, tup);
- if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex)
- {
- Relation idescs[Num_pg_type_indices];
-
- CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
- CatalogCloseIndices(Num_pg_type_indices, idescs);
- }
- RelationUnsetLockForWrite(pg_type_desc);
- heap_close(pg_type_desc);
+ /*
+ * 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;
+ return typeObjectId;
}
-/* ----------------------------------------------------------------
- * TypeRename
+/*
+ * GenerateTypeDependencies: build the dependencies needed for a type
*
- * This renames 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
-TypeRename(char *oldTypeName, char *newTypeName)
+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)
{
- Relation pg_type_desc;
- Relation idescs[Num_pg_type_indices];
- Oid type_oid;
- HeapTuple tup;
- bool defined;
- ItemPointerData itemPointerData;
+ ObjectAddress myself,
+ referenced;
- /* check that that the new type is not already defined */
- type_oid = TypeGet(newTypeName, &defined);
- if (OidIsValid(type_oid) && defined)
+ if (rebuild)
{
- elog(WARN, "TypeRename: type %s already defined", newTypeName);
+ deleteDependencyRecordsFor(TypeRelationId, typeObjectId);
+ deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId);
}
- /* get the type tuple from the catalog index scan manager */
- pg_type_desc = heap_openr(TypeRelationName);
- tup = TypeNameIndexScan(pg_type_desc, oldTypeName);
+ myself.classId = TypeRelationId;
+ myself.objectId = typeObjectId;
+ myself.objectSubId = 0;
- /* ----------------
- * change the name of the type
- * ----------------
- */
- if (HeapTupleIsValid(tup))
+ /* dependency on namespace */
+ /* skip for relation rowtype, since we have indirect dependency */
+ if (!OidIsValid(relationOid))
{
+ referenced.classId = NamespaceRelationId;
+ referenced.objectId = typeNamespace;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- namestrcpy(&(((TypeTupleForm) GETSTRUCT(tup))->typname), newTypeName);
+ /* Normal dependencies on the I/O functions */
+ if (OidIsValid(inputProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = inputProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ if (OidIsValid(outputProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = outputProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- setheapoverride(true);
- heap_replace(pg_type_desc, &itemPointerData, tup);
- setheapoverride(false);
+ if (OidIsValid(receiveProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = receiveProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- /* update the system catalog indices */
- CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
- CatalogCloseIndices(Num_pg_type_indices, idescs);
+ if (OidIsValid(sendProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = sendProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
- /* all done */
- pfree(tup);
+ if (OidIsValid(analyzeProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = analyzeProcedure;
+ 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))
+ {
+ referenced.classId = RelationRelationId;
+ referenced.objectId = relationOid;
+ referenced.objectSubId = 0;
+
+ if (relationKind != RELKIND_COMPOSITE_TYPE)
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+ else
+ recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
}
- else
+
+ /*
+ * 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))
{
- elog(WARN, "TypeRename: type %s not defined", oldTypeName);
+ referenced.classId = TypeRelationId;
+ referenced.objectId = elementType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
- /* finish up */
- heap_close(pg_type_desc);
+ /* 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);
+}
+
+/*
+ * TypeRename
+ * This renames a type
+ *
+ * Note: any associated array type is *not* renamed; caller must make
+ * another call to handle that case. Currently this is only used for
+ * renaming types associated with tables, for which there are no arrays.
+ */
+void
+TypeRename(const char *oldTypeName, Oid typeNamespace,
+ const char *newTypeName)
+{
+ Relation pg_type_desc;
+ HeapTuple tuple;
+
+ pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy(TYPENAMENSP,
+ CStringGetDatum(oldTypeName),
+ ObjectIdGetDatum(typeNamespace),
+ 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" does not exist", oldTypeName)));
+
+ if (SearchSysCacheExists(TYPENAMENSP,
+ CStringGetDatum(newTypeName),
+ ObjectIdGetDatum(typeNamespace),
+ 0, 0))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists", newTypeName)));
+
+ namestrcpy(&(((Form_pg_type) GETSTRUCT(tuple))->typname), newTypeName);
+
+ simple_heap_update(pg_type_desc, &tuple->t_self, tuple);
+
+ /* update the system catalog indexes */
+ CatalogUpdateIndexes(pg_type_desc, tuple);
+
+ heap_freetuple(tuple);
+ heap_close(pg_type_desc, RowExclusiveLock);
}
/*
* makeArrayTypeName(typeName);
* - given a base type name, make an array of type name out of it
*
- * the CALLER is responsible for pfreeing the
+ * the caller is responsible for pfreeing the result
*/
-
-char *
-makeArrayTypeName(char *typeName)
+char *
+makeArrayTypeName(const char *typeName)
{
char *arr;
if (!typeName)
return NULL;
- arr = palloc(strlen(typeName) + 2);
- arr[0] = '_';
- strcpy(arr + 1, typeName);
-
+ arr = palloc(NAMEDATALEN);
+ snprintf(arr, NAMEDATALEN,
+ "_%.*s", NAMEDATALEN - 2, typeName);
return arr;
-
}