mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-24 02:47:19 +02:00
bothering to check the return value --- which meant that in case the update or delete failed because of a concurrent update, you'd not find out about it, except by observing later that the transaction produced the wrong outcome. There are now subroutines simple_heap_update and simple_heap_delete that should be used anyplace that you're not prepared to do the full nine yards of coping with concurrent updates. In practice, that seems to mean absolutely everywhere but the executor, because *noplace* else was checking.
590 lines
15 KiB
C
590 lines
15 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pg_type.c
|
|
* routines to support manipulation of the pg_type relation
|
|
*
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.57 2001/01/23 04:32:21 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/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;
|
|
|
|
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 dummy values
|
|
* ----------------
|
|
*/
|
|
i = 0;
|
|
namestrcpy(&name, typeName);
|
|
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);
|
|
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
|
|
* ----------------
|
|
*/
|
|
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.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
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,
|
|
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];
|
|
|
|
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++] = Int32GetDatum(GetUserId()); /* 2 */
|
|
values[i++] = Int16GetDatum(internalSize); /* 3 */
|
|
values[i++] = Int16GetDatum(externalSize); /* 4 */
|
|
values[i++] = BoolGetDatum(passedByValue); /* 5 */
|
|
values[i++] = CharGetDatum(typeType); /* 6 */
|
|
values[i++] = BoolGetDatum(true); /* 7 */
|
|
values[i++] = CharGetDatum(typDelim); /* 8 */
|
|
values[i++] = ObjectIdGetDatum(typeType == 'c' ? relationOid : InvalidOid); /* 9 */
|
|
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)
|
|
{
|
|
Oid procOid;
|
|
|
|
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, FUNC_MAX_ARGS * sizeof(Oid));
|
|
|
|
procOid = GetSysCacheOid(PROCNAME,
|
|
PointerGetDatum(procname),
|
|
Int32GetDatum(1),
|
|
PointerGetDatum(argList),
|
|
0);
|
|
|
|
if (!OidIsValid(procOid))
|
|
{
|
|
|
|
/*
|
|
* 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))
|
|
{
|
|
int nargs;
|
|
|
|
if (j % 2)
|
|
{
|
|
/* output proc */
|
|
nargs = 2;
|
|
argList[1] = OIDOID;
|
|
}
|
|
else
|
|
{
|
|
/* input proc */
|
|
nargs = 3;
|
|
argList[1] = OIDOID;
|
|
argList[2] = INT4OID;
|
|
}
|
|
procOid = GetSysCacheOid(PROCNAME,
|
|
PointerGetDatum(procname),
|
|
Int32GetDatum(nargs),
|
|
PointerGetDatum(argList),
|
|
0);
|
|
}
|
|
if (!OidIsValid(procOid))
|
|
func_error("TypeCreate", procname, 1, argList, NULL);
|
|
}
|
|
|
|
values[i++] = ObjectIdGetDatum(procOid); /* 11 - 14 */
|
|
}
|
|
|
|
/* ----------------
|
|
* set default alignment
|
|
* ----------------
|
|
*/
|
|
values[i++] = CharGetDatum(alignment); /* 15 */
|
|
|
|
/* ----------------
|
|
* set default storage for TOAST
|
|
* ----------------
|
|
*/
|
|
values[i++] = CharGetDatum(storage); /* 16 */
|
|
|
|
/* ----------------
|
|
* initialize the default value for this type.
|
|
* ----------------
|
|
*/
|
|
values[i] = DirectFunctionCall1(textin, /* 17 */
|
|
CStringGetDatum(defaultTypeValue ? defaultTypeValue : "-"));
|
|
|
|
/* ----------------
|
|
* 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);
|
|
|
|
simple_heap_update(pg_type_desc, &tup->t_self, tup);
|
|
|
|
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(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))
|
|
elog(ERROR, "TypeRename: type \"%s\" not defined", oldTypeName);
|
|
|
|
if (SearchSysCacheExists(TYPENAME,
|
|
PointerGetDatum(newTypeName),
|
|
0, 0, 0))
|
|
elog(ERROR, "TypeRename: type \"%s\" already defined", 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
|
|
*/
|
|
|
|
char *
|
|
makeArrayTypeName(char *typeName)
|
|
{
|
|
char *arr;
|
|
|
|
if (!typeName)
|
|
return NULL;
|
|
arr = palloc(strlen(typeName) + 2);
|
|
arr[0] = '_';
|
|
strcpy(arr + 1, typeName);
|
|
|
|
return arr;
|
|
|
|
}
|