postgresql/src/backend/catalog/pg_type.c
Tom Lane bd272cace6 Mega-commit to make heap_open/heap_openr/heap_close take an
additional argument specifying the kind of lock to acquire/release (or
'NoLock' to do no lock processing).  Ensure that all relations are locked
with some appropriate lock level before being examined --- this ensures
that relevant shared-inval messages have been processed and should prevent
problems caused by concurrent VACUUM.  Fix several bugs having to do with
mismatched increment/decrement of relation ref count and mismatched
heap_open/close (which amounts to the same thing).  A bogus ref count on
a relation doesn't matter much *unless* a SI Inval message happens to
arrive at the wrong time, which is probably why we got away with this
sloppiness for so long.  Repair missing grab of AccessExclusiveLock in
DROP TABLE, ALTER/RENAME TABLE, etc, as noted by Hiroshi.
Recommend 'make clean all' after pulling this update; I modified the
Relation struct layout slightly.
Will post further discussion to pghackers list shortly.
1999-09-18 19:08:25 +00:00

595 lines
14 KiB
C

/*-------------------------------------------------------------------------
*
* pg_type.c
* routines to support manipulation of the pg_type relation
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.41 1999/09/18 19:06:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
static Oid TypeShellMakeWithOpenRelation(Relation pg_type_desc,
char *typeName);
/* ----------------------------------------------------------------
* 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;
Oid typoid;
static ScanKeyData typeKey[1] = {
{0, Anum_pg_type_typname, F_NAMEEQ}
};
/* ----------------
* initialize the scan key and begin a scan of pg_type
* ----------------
*/
fmgr_info(F_NAMEEQ, &typeKey[0].sk_func);
typeKey[0].sk_nargs = typeKey[0].sk_func.fn_nargs;
typeKey[0].sk_argument = PointerGetDatum(typeName);
scan = heap_beginscan(pg_type_desc,
0,
SnapshotSelf, /* cache? */
1,
typeKey);
/* ----------------
* get the type tuple, if it exists.
* ----------------
*/
tup = heap_getnext(scan, 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.
* ----------------
*/
*defined = (bool) ((Form_pg_type) GETSTRUCT(tup))->typisdefined;
typoid = tup->t_data->t_oid;
heap_endscan(scan);
return typoid;
}
/* ----------------------------------------------------------------
* 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.
*
* Note: the meat of this function is now in the function
* TypeGetWithOpenRelation(). -cim 6/15/90
*
* Also called from util/remove.c
* ----------------------------------------------------------------
*/
Oid
TypeGet(char *typeName, /* name of type to be fetched */
bool *defined) /* has the type been defined? */
{
Relation pg_type_desc;
Oid typeoid;
/* ----------------
* open the pg_type relation
* ----------------
*/
pg_type_desc = heap_openr(TypeRelationName, AccessShareLock);
/* ----------------
* 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, AccessShareLock);
return typeoid;
}
/* ----------------------------------------------------------------
* TypeShellMakeWithOpenRelation
*
* ----------------------------------------------------------------
*/
static Oid
TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName)
{
int i;
HeapTuple tup;
Datum values[Natts_pg_type];
char nulls[Natts_pg_type];
Oid typoid;
NameData name;
TupleDesc tupDesc;
/* ----------------
* initialize our *nulls and *values arrays
* ----------------
*/
for (i = 0; i < Natts_pg_type; ++i)
{
nulls[i] = ' ';
values[i] = (Datum) NULL; /* redundant, but safe */
}
/* ----------------
* initialize *values with the type name and
* ----------------
*/
i = 0;
namestrcpy(&name, typeName);
values[i++] = NameGetDatum(&name); /* 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 */
/*
* ... and fill typdefault with a bogus value
*/
values[i++] = (Datum) fmgr(F_TEXTIN, typeName); /* 15 */
/* ----------------
* create a new type tuple with FormHeapTuple
* ----------------
*/
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_data->t_oid;
if (RelationGetForm(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
* ----------------
*/
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;
Assert(PointerIsValid(typeName));
/* ----------------
* open pg_type
* ----------------
*/
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
/* ----------------
* insert the shell tuple
* ----------------
*/
typoid = TypeShellMakeWithOpenRelation(pg_type_desc, typeName);
/* ----------------
* close pg_type and return the tuple's oid.
* ----------------
*/
heap_close(pg_type_desc, RowExclusiveLock);
return typoid;
}
/* ----------------------------------------------------------------
* TypeCreate
*
* This does all the necessary work needed to define a new type.
* ----------------------------------------------------------------
*/
Oid
TypeCreate(char *typeName,
Oid relationOid, /* only for 'c'atalog typeTypes */
int16 internalSize,
int16 externalSize,
char typeType,
char typDelim,
char *inputProcedure,
char *outputProcedure,
char *receiveProcedure,
char *sendProcedure,
char *elementTypeName,
char *defaultTypeValue, /* internal rep */
bool passedByValue,
char alignment)
{
int i,
j;
Relation pg_type_desc;
HeapScanDesc pg_type_scan;
Oid typeObjectId;
Oid elementObjectId = InvalidOid;
HeapTuple tup;
char nulls[Natts_pg_type];
char replaces[Natts_pg_type];
Datum values[Natts_pg_type];
char *procname;
char *procs[4];
bool defined;
NameData name;
TupleDesc tupDesc;
Oid argList[8];
static ScanKeyData typeKey[1] = {
{0, Anum_pg_type_typname, F_NAMEEQ}
};
fmgr_info(F_NAMEEQ, &typeKey[0].sk_func);
typeKey[0].sk_nargs = typeKey[0].sk_func.fn_nargs;
/* ----------------
* check that the type is not already defined.
* ----------------
*/
typeObjectId = TypeGet(typeName, &defined);
if (OidIsValid(typeObjectId) && defined)
elog(ERROR, "TypeCreate: type %s already defined", typeName);
/* ----------------
* if this type has an associated elementType, then we check that
* it is defined.
* ----------------
*/
if (elementTypeName)
{
elementObjectId = TypeGet(elementTypeName, &defined);
if (!defined)
elog(ERROR, "TypeCreate: type %s is not defined", elementTypeName);
}
/* ----------------
* XXX comment me
* ----------------
*/
if (externalSize == 0)
externalSize = -1; /* variable length */
/* ----------------
* initialize arrays needed by FormHeapTuple
* ----------------
*/
for (i = 0; i < Natts_pg_type; ++i)
{
nulls[i] = ' ';
replaces[i] = 'r';
values[i] = (Datum) NULL; /* redundant, but nice */
}
/*
* 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
* ----------------
*/
i = 0;
namestrcpy(&name, typeName);
values[i++] = NameGetDatum(&name); /* 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 */
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];
/*
* First look for a 1-argument func with all argtypes 0. This is
* valid for all four kinds of procedure.
*/
MemSet(argList, 0, 8 * sizeof(Oid));
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(procname),
Int32GetDatum(1),
PointerGetDatum(argList),
0);
if (!HeapTupleIsValid(tup))
{
/*
* For array types, the input procedures may take 3 args (data
* value, element OID, atttypmod); the pg_proc argtype
* signature is 0,0,INT4OID. The output procedures may take 2
* args (data value, element OID).
*/
if (OidIsValid(elementObjectId))
{
int nargs;
if (j % 2)
{
/* output proc */
nargs = 2;
}
else
{
/* input proc */
nargs = 3;
argList[2] = INT4OID;
}
tup = SearchSysCacheTuple(PRONAME,
PointerGetDatum(procname),
Int32GetDatum(nargs),
PointerGetDatum(argList),
0);
}
if (!HeapTupleIsValid(tup))
func_error("TypeCreate", procname, 1, argList, NULL);
}
values[i++] = (Datum) tup->t_data->t_oid; /* 11 - 14 */
}
/* ----------------
* set default alignment
* ----------------
*/
values[i++] = (Datum) alignment; /* 15 */
/* ----------------
* initialize the default value for this type.
* ----------------
*/
values[i] = (Datum) fmgr(F_TEXTIN, /* 16 */
PointerIsValid(defaultTypeValue)
? defaultTypeValue : "-"); /* XXX default
* typdefault */
/* ----------------
* open pg_type and begin a scan for the type name.
* ----------------
*/
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
typeKey[0].sk_argument = PointerGetDatum(typeName);
pg_type_scan = heap_beginscan(pg_type_desc,
0,
SnapshotSelf, /* cache? */
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);
if (HeapTupleIsValid(tup))
{
tup = heap_modifytuple(tup,
pg_type_desc,
values,
nulls,
replaces);
setheapoverride(true);
heap_replace(pg_type_desc, &tup->t_self, tup, NULL);
setheapoverride(false);
typeObjectId = tup->t_data->t_oid;
}
else
{
tupDesc = pg_type_desc->rd_att;
tup = heap_formtuple(tupDesc,
values,
nulls);
heap_insert(pg_type_desc, tup);
typeObjectId = tup->t_data->t_oid;
}
/* ----------------
* finish up
* ----------------
*/
heap_endscan(pg_type_scan);
if (RelationGetForm(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);
}
heap_close(pg_type_desc, RowExclusiveLock);
return typeObjectId;
}
/* ----------------------------------------------------------------
* TypeRename
*
* This renames a type
* ----------------------------------------------------------------
*/
void
TypeRename(char *oldTypeName, char *newTypeName)
{
Relation pg_type_desc;
Relation idescs[Num_pg_type_indices];
HeapTuple oldtup,
newtup;
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
oldtup = SearchSysCacheTupleCopy(TYPNAME,
PointerGetDatum(oldTypeName),
0, 0, 0);
if (!HeapTupleIsValid(oldtup))
{
heap_close(pg_type_desc, RowExclusiveLock);
elog(ERROR, "TypeRename: type %s not defined", oldTypeName);
}
newtup = SearchSysCacheTuple(TYPNAME,
PointerGetDatum(newTypeName),
0, 0, 0);
if (HeapTupleIsValid(newtup))
{
pfree(oldtup);
heap_close(pg_type_desc, RowExclusiveLock);
elog(ERROR, "TypeRename: type %s already defined", newTypeName);
}
namestrcpy(&(((Form_pg_type) GETSTRUCT(oldtup))->typname), newTypeName);
setheapoverride(true);
heap_replace(pg_type_desc, &oldtup->t_self, oldtup, NULL);
setheapoverride(false);
/* update the system catalog indices */
CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, oldtup);
CatalogCloseIndices(Num_pg_type_indices, idescs);
pfree(oldtup);
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
*/
char *
makeArrayTypeName(char *typeName)
{
char *arr;
if (!typeName)
return NULL;
arr = palloc(strlen(typeName) + 2);
arr[0] = '_';
strcpy(arr + 1, typeName);
return arr;
}