/*------------------------------------------------------------------------- * * 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.69 2002/03/20 19:43:38 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; 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. */ *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++] = BoolGetDatum(false); /* 17 */ values[i++] = ObjectIdGetDatum(InvalidOid); /* 18 */ values[i++] = Int32GetDatum(-1); /* 19 */ values[i++] = Int32GetDatum(0); /* 20 */ nulls[i++] = 'n'; /* 21 */ nulls[i++] = 'n'; /* 22 */ /* * 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. * * 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 *baseTypeName, char *defaultTypeValue, /* human readable rep */ char *defaultTypeBin, /* cooked rep */ bool passedByValue, char alignment, char storage, int32 typeMod, int32 typNDims, /* Array dimensions for baseTypeName */ bool typeNotNull) { int i, j; Relation pg_type_desc; HeapScanDesc pg_type_scan; Oid typeObjectId; Oid elementObjectId = InvalidOid; Oid baseObjectId = 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)) 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) elog(ERROR, "type %s does not exist", elementTypeName); } /* * if this type has an associated baseType, then we check that it * is defined. */ if (baseTypeName) { baseObjectId = TypeGet(baseTypeName, &defined); if (!defined) elog(ERROR, "type %s does not exist", baseTypeName); } /* * 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); 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) || OidIsValid(baseObjectId)) { 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 */ /* set typnotnull, typbasetype, typtypmod, typndims */ values[i++] = BoolGetDatum(typeNotNull); /* 17 */ values[i++] = ObjectIdGetDatum(baseObjectId); /* 18 */ values[i++] = Int32GetDatum(typeMod); /* 19 */ values[i++] = Int32GetDatum(typNDims); /* 20 */ /* * 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++; /* 21 */ /* * initialize the default value for this type. */ if (defaultTypeValue) values[i] = DirectFunctionCall1(textin, CStringGetDatum(defaultTypeValue)); else nulls[i] = 'n'; i++; /* 22 */ /* * 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) 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); 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); 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, "type %s does not exist", oldTypeName); if (SearchSysCacheExists(TYPENAME, PointerGetDatum(newTypeName), 0, 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(char *typeName) { char *arr; if (!typeName) return NULL; arr = palloc(NAMEDATALEN); snprintf(arr, NAMEDATALEN, "_%.*s", NAMEDATALEN - 2, typeName); return arr; }