mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-24 03:57:19 +02:00
bitmap, if present). Per Tom Lane's suggestion the information whether a tuple has an oid or not is carried in the tuple descriptor. For debugging reasons tdhasoid is of type char, not bool. There are predefined values for WITHOID, WITHOUTOID and UNDEFOID. This patch has been generated against a cvs snapshot from last week and I don't expect it to apply cleanly to current sources. While I post it here for public review, I'm working on a new version against a current snapshot. (There's been heavy activity recently; hope to catch up some day ...) This is a long patch; if it is too hard to swallow, I can provide it in smaller pieces: Part 1: Accessor macros Part 2: tdhasoid in TupDesc Part 3: Regression test Part 4: Parameter withoid to heap_addheader Part 5: Eliminate t_oid from HeapTupleHeader Part 2 is the most hairy part because of changes in the executor and even in the parser; the other parts are straightforward. Up to part 4 the patched postmaster stays binary compatible to databases created with an unpatched version. Part 5 is small (100 lines) and finally breaks compatibility. Manfred Koizar
462 lines
14 KiB
C
462 lines
14 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pg_type.c
|
|
* routines to support manipulation of the pg_type relation
|
|
*
|
|
* Portions Copyright (c) 1996-2002, 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.75 2002/07/20 05:16:57 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/heapam.h"
|
|
#include "catalog/catname.h"
|
|
#include "catalog/dependency.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* 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(const char *typeName, Oid typeNamespace)
|
|
{
|
|
Relation pg_type_desc;
|
|
TupleDesc tupDesc;
|
|
int i;
|
|
HeapTuple tup;
|
|
Datum values[Natts_pg_type];
|
|
char nulls[Natts_pg_type];
|
|
Oid typoid;
|
|
NameData name;
|
|
|
|
Assert(PointerIsValid(typeName));
|
|
|
|
/*
|
|
* open pg_type
|
|
*/
|
|
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
|
|
tupDesc = pg_type_desc->rd_att;
|
|
|
|
/*
|
|
* 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); /* typname */
|
|
values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
|
|
values[i++] = ObjectIdGetDatum(InvalidOid); /* typowner */
|
|
values[i++] = Int16GetDatum(0); /* typlen */
|
|
values[i++] = Int16GetDatum(0); /* typprtlen */
|
|
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++] = 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 */
|
|
|
|
/*
|
|
* create a new type tuple
|
|
*/
|
|
tup = heap_formtuple(tupDesc, values, nulls);
|
|
|
|
/*
|
|
* insert the tuple in the relation and get the tuple's oid.
|
|
*/
|
|
typoid = simple_heap_insert(pg_type_desc, tup);
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* clean up and return the type-oid
|
|
*/
|
|
heap_freetuple(tup);
|
|
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(const char *typeName,
|
|
Oid typeNamespace,
|
|
Oid assignedTypeOid,
|
|
Oid relationOid, /* only for 'c'atalog typeTypes */
|
|
int16 internalSize,
|
|
int16 externalSize,
|
|
char typeType,
|
|
char typDelim,
|
|
Oid inputProcedure,
|
|
Oid outputProcedure,
|
|
Oid receiveProcedure,
|
|
Oid sendProcedure,
|
|
Oid elementType,
|
|
Oid baseType,
|
|
const char *defaultTypeValue, /* human readable rep */
|
|
const char *defaultTypeBin, /* cooked rep */
|
|
bool passedByValue,
|
|
char alignment,
|
|
char storage,
|
|
int32 typeMod,
|
|
int32 typNDims, /* Array dimensions for baseType */
|
|
bool typeNotNull)
|
|
{
|
|
Relation pg_type_desc;
|
|
Oid typeObjectId;
|
|
HeapTuple tup;
|
|
char nulls[Natts_pg_type];
|
|
char replaces[Natts_pg_type];
|
|
Datum values[Natts_pg_type];
|
|
NameData name;
|
|
TupleDesc tupDesc;
|
|
int i;
|
|
|
|
/*
|
|
* 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 for heap_formtuple or heap_modifytuple
|
|
*/
|
|
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);
|
|
values[i++] = NameGetDatum(&name); /* typname */
|
|
values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
|
|
values[i++] = Int32GetDatum(GetUserId()); /* typowner */
|
|
values[i++] = Int16GetDatum(internalSize); /* typlen */
|
|
values[i++] = Int16GetDatum(externalSize); /* typprtlen */
|
|
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++] = 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 */
|
|
|
|
/*
|
|
* initialize the default binary value for this type. Check for
|
|
* nulls of course.
|
|
*/
|
|
if (defaultTypeBin)
|
|
values[i] = DirectFunctionCall1(textin,
|
|
CStringGetDatum(defaultTypeBin));
|
|
else
|
|
nulls[i] = 'n';
|
|
i++; /* typdefaultbin */
|
|
|
|
/*
|
|
* initialize the default value for this type.
|
|
*/
|
|
if (defaultTypeValue)
|
|
values[i] = DirectFunctionCall1(textin,
|
|
CStringGetDatum(defaultTypeValue));
|
|
else
|
|
nulls[i] = 'n';
|
|
i++; /* typdefault */
|
|
|
|
/*
|
|
* 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, RowExclusiveLock);
|
|
|
|
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 (but only if assignedTypeOid is not given).
|
|
*/
|
|
if (((Form_pg_type) GETSTRUCT(tup))->typisdefined ||
|
|
assignedTypeOid != InvalidOid)
|
|
elog(ERROR, "type %s already exists", typeName);
|
|
|
|
/*
|
|
* Okay to update existing "shell" type tuple
|
|
*/
|
|
tup = heap_modifytuple(tup,
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
tupDesc = pg_type_desc->rd_att;
|
|
|
|
tup = heap_formtuple(tupDesc,
|
|
values,
|
|
nulls);
|
|
|
|
/* preassign tuple Oid, if one was given */
|
|
AssertTupleDescHasOid(tupDesc);
|
|
HeapTupleSetOid(tup, assignedTypeOid);
|
|
|
|
typeObjectId = simple_heap_insert(pg_type_desc, tup);
|
|
}
|
|
|
|
/* Update indices (not necessary if bootstrapping) */
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Create dependencies. We can/must skip this in bootstrap mode.
|
|
*/
|
|
if (!IsBootstrapProcessingMode())
|
|
{
|
|
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.objectId = inputProcedure;
|
|
referenced.objectSubId = 0;
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
referenced.classId = RelOid_pg_proc;
|
|
referenced.objectId = outputProcedure;
|
|
referenced.objectSubId = 0;
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
if (receiveProcedure != inputProcedure)
|
|
{
|
|
referenced.classId = RelOid_pg_proc;
|
|
referenced.objectId = receiveProcedure;
|
|
referenced.objectSubId = 0;
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
}
|
|
|
|
if (sendProcedure != outputProcedure)
|
|
{
|
|
referenced.classId = RelOid_pg_proc;
|
|
referenced.objectId = sendProcedure;
|
|
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. This allows it to be auto-dropped
|
|
* when the relation is, and not otherwise.
|
|
*/
|
|
if (OidIsValid(relationOid))
|
|
{
|
|
referenced.classId = RelOid_pg_class;
|
|
referenced.objectId = relationOid;
|
|
referenced.objectSubId = 0;
|
|
recordDependencyOn(&myself, &referenced, 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 = 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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* finish up
|
|
*/
|
|
heap_close(pg_type_desc, RowExclusiveLock);
|
|
|
|
return typeObjectId;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
Relation idescs[Num_pg_type_indices];
|
|
HeapTuple tuple;
|
|
|
|
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
|
|
|
|
tuple = SearchSysCacheCopy(TYPENAMENSP,
|
|
CStringGetDatum(oldTypeName),
|
|
ObjectIdGetDatum(typeNamespace),
|
|
0, 0);
|
|
if (!HeapTupleIsValid(tuple))
|
|
elog(ERROR, "type %s does not exist", oldTypeName);
|
|
|
|
if (SearchSysCacheExists(TYPENAMENSP,
|
|
CStringGetDatum(newTypeName),
|
|
ObjectIdGetDatum(typeNamespace),
|
|
0, 0))
|
|
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(const char *typeName)
|
|
{
|
|
char *arr;
|
|
|
|
if (!typeName)
|
|
return NULL;
|
|
arr = palloc(NAMEDATALEN);
|
|
snprintf(arr, NAMEDATALEN,
|
|
"_%.*s", NAMEDATALEN - 2, typeName);
|
|
return arr;
|
|
}
|