postgresql/src/backend/catalog/pg_type.c

571 lines
14 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* pg_type.c
* routines to support manipulation of the pg_type relation
*
* 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/catalog/pg_type.c,v 1.67 2002/03/07 16:35:33 momjian 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/fmgroids.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;
ScanKeyData typeKey[1];
/*
* initialize the scan key and begin a scan of pg_type
*/
ScanKeyEntryInitialize(&typeKey[0],
0,
Anum_pg_type_typname,
F_NAMEEQ,
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.
*/
1998-09-01 05:29:17 +02:00
*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 dummy values
*/
i = 0;
namestrcpy(&name, typeName);
2001-03-22 05:01:46 +01:00
values[i++] = NameGetDatum(&name); /* 1 */
values[i++] = ObjectIdGetDatum(InvalidOid); /* 2 */
values[i++] = Int16GetDatum(0); /* 3 */
values[i++] = Int16GetDatum(0); /* 4 */
values[i++] = BoolGetDatum(false); /* 5 */
values[i++] = CharGetDatum(0); /* 6 */
values[i++] = BoolGetDatum(false); /* 7 */
values[i++] = CharGetDatum(0); /* 8 */
values[i++] = ObjectIdGetDatum(InvalidOid); /* 9 */
values[i++] = ObjectIdGetDatum(InvalidOid); /* 10 */
values[i++] = ObjectIdGetDatum(InvalidOid); /* 11 */
values[i++] = ObjectIdGetDatum(InvalidOid); /* 12 */
values[i++] = ObjectIdGetDatum(InvalidOid); /* 13 */
values[i++] = ObjectIdGetDatum(InvalidOid); /* 14 */
values[i++] = CharGetDatum('i'); /* 15 */
values[i++] = CharGetDatum('p'); /* 16 */
values[i++] = DirectFunctionCall1(textin,
CStringGetDatum(typeName)); /* 17 */
/*
* 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);
1998-11-27 20:52:36 +01:00
typoid = tup->t_data->t_oid;
1998-09-01 05:29:17 +02:00
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
*/
heap_freetuple(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.
*
* 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.
* ----------------------------------------------------------------
*/
Oid
TypeCreate(char *typeName,
Oid assignedTypeOid,
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,
char storage)
{
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[FUNC_MAX_ARGS];
ScanKeyData typeKey[1];
/*
* check that the type is not already defined. It might exist as a
* shell type, however (but only if assignedTypeOid is not given).
*/
typeObjectId = TypeGet(typeName, &defined);
if (OidIsValid(typeObjectId) &&
(defined || assignedTypeOid != InvalidOid))
2001-08-10 17:49:39 +02:00
elog(ERROR, "type named %s already exists", typeName);
/*
* if this type has an associated elementType, then we check that it
* is defined.
*/
if (elementTypeName)
{
elementObjectId = TypeGet(elementTypeName, &defined);
if (!defined)
2001-08-10 17:49:39 +02:00
elog(ERROR, "type %s does not exist", elementTypeName);
}
/*
* validate size specifications: either positive (fixed-length) or -1
* (variable-length).
*/
if (!(internalSize > 0 || internalSize == -1))
elog(ERROR, "TypeCreate: invalid type internal size %d",
internalSize);
if (!(externalSize > 0 || externalSize == -1))
elog(ERROR, "TypeCreate: invalid type external size %d",
externalSize);
if (internalSize != -1 && storage != 'p')
elog(ERROR, "TypeCreate: fixed size types must have storage PLAIN");
/*
* initialize arrays needed by FormHeapTuple
*/
for (i = 0; i < Natts_pg_type; ++i)
{
nulls[i] = ' ';
replaces[i] = 'r';
values[i] = (Datum) 0;
}
/*
* initialize the *values information
*/
i = 0;
namestrcpy(&name, typeName);
2001-03-22 05:01:46 +01:00
values[i++] = NameGetDatum(&name); /* 1 */
values[i++] = Int32GetDatum(GetUserId()); /* 2 */
values[i++] = Int16GetDatum(internalSize); /* 3 */
values[i++] = Int16GetDatum(externalSize); /* 4 */
values[i++] = BoolGetDatum(passedByValue); /* 5 */
values[i++] = CharGetDatum(typeType); /* 6 */
2001-03-22 05:01:46 +01:00
values[i++] = BoolGetDatum(true); /* 7 */
values[i++] = CharGetDatum(typDelim); /* 8 */
values[i++] = ObjectIdGetDatum(typeType == 'c' ? relationOid : InvalidOid); /* 9 */
2001-03-22 05:01:46 +01:00
values[i++] = ObjectIdGetDatum(elementObjectId); /* 10 */
procs[0] = inputProcedure;
procs[1] = outputProcedure;
procs[2] = (receiveProcedure) ? receiveProcedure : inputProcedure;
procs[3] = (sendProcedure) ? sendProcedure : outputProcedure;
for (j = 0; j < 4; ++j)
{
2001-03-22 05:01:46 +01:00
Oid procOid;
procname = procs[j];
/*
1999-05-25 18:15:34 +02:00
* First look for a 1-argument func with all argtypes 0. This is
* valid for all four kinds of procedure.
*/
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
procOid = GetSysCacheOid(PROCNAME,
PointerGetDatum(procname),
Int32GetDatum(1),
PointerGetDatum(argList),
0);
if (!OidIsValid(procOid))
{
/*
1999-05-25 18:15:34 +02:00
* For array types, the input procedures may take 3 args (data
* value, element OID, atttypmod); the pg_proc argtype
* signature is 0,OIDOID,INT4OID. The output procedures may
* take 2 args (data value, element OID).
*/
if (OidIsValid(elementObjectId))
{
1999-05-25 18:15:34 +02:00
int nargs;
if (j % 2)
{
/* output proc */
nargs = 2;
argList[1] = OIDOID;
1999-05-25 18:15:34 +02:00
}
else
{
/* input proc */
nargs = 3;
argList[1] = OIDOID;
argList[2] = INT4OID;
}
procOid = GetSysCacheOid(PROCNAME,
PointerGetDatum(procname),
Int32GetDatum(nargs),
PointerGetDatum(argList),
0);
}
if (!OidIsValid(procOid))
1998-05-10 01:43:45 +02:00
func_error("TypeCreate", procname, 1, argList, NULL);
}
2001-03-22 05:01:46 +01:00
values[i++] = ObjectIdGetDatum(procOid); /* 11 - 14 */
}
/*
* set default alignment
*/
2001-03-22 05:01:46 +01:00
values[i++] = CharGetDatum(alignment); /* 15 */
/*
* set default storage for TOAST
*/
2001-03-22 05:01:46 +01:00
values[i++] = CharGetDatum(storage); /* 16 */
/*
* initialize the default value for this type.
*/
if (defaultTypeValue)
values[i] = DirectFunctionCall1(textin,
CStringGetDatum(defaultTypeValue));
else
nulls[i] = 'n';
i++; /* 17 */
/*
* open pg_type and begin a scan for the type name.
*/
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&typeKey[0],
0,
Anum_pg_type_typname,
F_NAMEEQ,
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))
{
/* should not happen given prior test? */
if (assignedTypeOid != InvalidOid)
2001-08-10 17:49:39 +02:00
elog(ERROR, "type %s already exists", typeName);
tup = heap_modifytuple(tup,
pg_type_desc,
values,
nulls,
replaces);
simple_heap_update(pg_type_desc, &tup->t_self, tup);
1998-11-27 20:52:36 +01:00
typeObjectId = tup->t_data->t_oid;
}
else
{
tupDesc = pg_type_desc->rd_att;
tup = heap_formtuple(tupDesc,
values,
nulls);
/* preassign tuple Oid, if one was given */
tup->t_data->t_oid = assignedTypeOid;
heap_insert(pg_type_desc, tup);
1998-11-27 20:52:36 +01:00
typeObjectId = tup->t_data->t_oid;
}
/*
* finish up
*/
heap_endscan(pg_type_scan);
1998-09-01 05:29:17 +02:00
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(const char *oldTypeName, const char *newTypeName)
{
Relation pg_type_desc;
Relation idescs[Num_pg_type_indices];
HeapTuple tuple;
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
tuple = SearchSysCacheCopy(TYPENAME,
PointerGetDatum(oldTypeName),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
2001-08-10 17:49:39 +02:00
elog(ERROR, "type %s does not exist", oldTypeName);
if (SearchSysCacheExists(TYPENAME,
PointerGetDatum(newTypeName),
0, 0, 0))
2001-08-10 17:49:39 +02:00
elog(ERROR, "type named %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 indices */
CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tuple);
CatalogCloseIndices(Num_pg_type_indices, idescs);
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 result
*/
char *
makeArrayTypeName(char *typeName)
{
char *arr;
if (!typeName)
return NULL;
arr = palloc(NAMEDATALEN);
snprintf(arr, NAMEDATALEN,
"_%.*s", NAMEDATALEN - 2, typeName);
return arr;
}