#include "postgres.h"#include "access/htup_details.h"#include "catalog/namespace.h"#include "catalog/pg_type.h"#include "lib/stringinfo.h"#include "nodes/makefuncs.h"#include "parser/parser.h"#include "parser/parse_type.h"#include "utils/array.h"#include "utils/builtins.h"#include "utils/datum.h"#include "utils/lsyscache.h"#include "utils/syscache.h"
Go to the source code of this file.
| static void appendTypeNameToBuffer | ( | const TypeName * | typeName, | |
| StringInfo | string | |||
| ) | [static] |
Definition at line 369 of file parse_type.c.
References appendStringInfoChar(), appendStringInfoString(), TypeName::arrayBounds, format_type_be(), lfirst, list_head(), TypeName::names, NIL, TypeName::pct_type, strVal, and TypeName::typeOid.
Referenced by TypeNameListToString(), and TypeNameToString().
{
if (typeName->names != NIL)
{
/* Emit possibly-qualified name as-is */
ListCell *l;
foreach(l, typeName->names)
{
if (l != list_head(typeName->names))
appendStringInfoChar(string, '.');
appendStringInfoString(string, strVal(lfirst(l)));
}
}
else
{
/* Look up internally-specified type */
appendStringInfoString(string, format_type_be(typeName->typeOid));
}
/*
* Add decoration as needed, but only for fields considered by
* LookupTypeName
*/
if (typeName->pct_type)
appendStringInfoString(string, "%TYPE");
if (typeName->arrayBounds != NIL)
appendStringInfoString(string, "[]");
}
| Oid GetColumnDefCollation | ( | ParseState * | pstate, | |
| ColumnDef * | coldef, | |||
| Oid | typeOid | |||
| ) |
Definition at line 471 of file parse_type.c.
References ColumnDef::collClause, CollateClause::collname, ColumnDef::collOid, ereport, errcode(), errmsg(), ERROR, format_type_be(), get_typcollation(), CollateClause::location, LookupCollation(), OidIsValid, and parser_errposition().
Referenced by addRangeTableEntryForFunction(), ATExecAddColumn(), ATExecAlterColumnType(), ATPrepAlterColumnType(), BuildDescForRelation(), and MergeAttributes().
{
Oid result;
Oid typcollation = get_typcollation(typeOid);
int location = -1;
if (coldef->collClause)
{
/* We have a raw COLLATE clause, so look up the collation */
location = coldef->collClause->location;
result = LookupCollation(pstate, coldef->collClause->collname,
location);
}
else if (OidIsValid(coldef->collOid))
{
/* Precooked collation spec, use that */
result = coldef->collOid;
}
else
{
/* Use the type's default collation if any */
result = typcollation;
}
/* Complain if COLLATE is applied to an uncollatable type */
if (OidIsValid(result) && !OidIsValid(typcollation))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("collations are not supported by type %s",
format_type_be(typeOid)),
parser_errposition(pstate, location)));
return result;
}
| Oid LookupCollation | ( | ParseState * | pstate, | |
| List * | collnames, | |||
| int | location | |||
| ) |
Definition at line 446 of file parse_type.c.
References cancel_parser_errposition_callback(), get_collation_oid(), and setup_parser_errposition_callback().
Referenced by GetColumnDefCollation(), transformCollateClause(), and transformColumnType().
{
Oid colloid;
ParseCallbackState pcbstate;
if (pstate)
setup_parser_errposition_callback(&pcbstate, pstate, location);
colloid = get_collation_oid(collnames, false);
if (pstate)
cancel_parser_errposition_callback(&pcbstate);
return colloid;
}
| Type LookupTypeName | ( | ParseState * | pstate, | |
| const TypeName * | typeName, | |||
| int32 * | typmod_p | |||
| ) |
Definition at line 58 of file parse_type.c.
References TypeName::arrayBounds, Assert, RangeVar::catalogname, DeconstructQualifiedName(), elog, ereport, errcode(), errmsg(), ERROR, format_type_be(), get_array_type(), get_attnum(), get_atttype(), GetSysCacheOid2, HeapTupleIsValid, InvalidAttrNumber, lfourth, linitial, list_length(), TypeName::location, LookupExplicitNamespace(), lsecond, lthird, makeRangeVar(), NameListToString(), TypeName::names, NIL, NoLock, NOTICE, NULL, ObjectIdGetDatum, OidIsValid, parser_errposition(), TypeName::pct_type, PointerGetDatum, RangeVarGetRelid, RangeVar::relname, RangeVar::schemaname, SearchSysCache1, strVal, TypenameGetTypid(), TYPENAMENSP, TypeNameToString(), typenameTypeMod(), TYPEOID, and TypeName::typeOid.
Referenced by AlterTypeOwner(), compute_return_type(), examine_parameter_list(), FuncNameAsType(), get_object_address_type(), LookupTypeNameOid(), plpgsql_parse_wordtype(), and typenameType().
{
Oid typoid;
HeapTuple tup;
int32 typmod;
if (typeName->names == NIL)
{
/* We have the OID already if it's an internally generated TypeName */
typoid = typeName->typeOid;
}
else if (typeName->pct_type)
{
/* Handle %TYPE reference to type of an existing field */
RangeVar *rel = makeRangeVar(NULL, NULL, typeName->location);
char *field = NULL;
Oid relid;
AttrNumber attnum;
/* deconstruct the name list */
switch (list_length(typeName->names))
{
case 1:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper %%TYPE reference (too few dotted names): %s",
NameListToString(typeName->names)),
parser_errposition(pstate, typeName->location)));
break;
case 2:
rel->relname = strVal(linitial(typeName->names));
field = strVal(lsecond(typeName->names));
break;
case 3:
rel->schemaname = strVal(linitial(typeName->names));
rel->relname = strVal(lsecond(typeName->names));
field = strVal(lthird(typeName->names));
break;
case 4:
rel->catalogname = strVal(linitial(typeName->names));
rel->schemaname = strVal(lsecond(typeName->names));
rel->relname = strVal(lthird(typeName->names));
field = strVal(lfourth(typeName->names));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper %%TYPE reference (too many dotted names): %s",
NameListToString(typeName->names)),
parser_errposition(pstate, typeName->location)));
break;
}
/*
* Look up the field.
*
* XXX: As no lock is taken here, this might fail in the presence of
* concurrent DDL. But taking a lock would carry a performance
* penalty and would also require a permissions check.
*/
relid = RangeVarGetRelid(rel, NoLock, false);
attnum = get_attnum(relid, field);
if (attnum == InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
field, rel->relname),
parser_errposition(pstate, typeName->location)));
typoid = get_atttype(relid, attnum);
/* this construct should never have an array indicator */
Assert(typeName->arrayBounds == NIL);
/* emit nuisance notice (intentionally not errposition'd) */
ereport(NOTICE,
(errmsg("type reference %s converted to %s",
TypeNameToString(typeName),
format_type_be(typoid))));
}
else
{
/* Normal reference to a type name */
char *schemaname;
char *typname;
/* deconstruct the name list */
DeconstructQualifiedName(typeName->names, &schemaname, &typname);
if (schemaname)
{
/* Look in specific schema only */
Oid namespaceId;
namespaceId = LookupExplicitNamespace(schemaname, false);
typoid = GetSysCacheOid2(TYPENAMENSP,
PointerGetDatum(typname),
ObjectIdGetDatum(namespaceId));
}
else
{
/* Unqualified type name, so search the search path */
typoid = TypenameGetTypid(typname);
}
/* If an array reference, return the array type instead */
if (typeName->arrayBounds != NIL)
typoid = get_array_type(typoid);
}
if (!OidIsValid(typoid))
{
if (typmod_p)
*typmod_p = -1;
return NULL;
}
tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for type %u", typoid);
typmod = typenameTypeMod(pstate, typeName, (Type) tup);
if (typmod_p)
*typmod_p = typmod;
return (Type) tup;
}
Definition at line 664 of file parse_type.c.
References appendStringInfo(), TypeCast::arg, ErrorContextCallback::arg, buf, ErrorContextCallback::callback, StringInfoData::data, SelectStmt::distinctClause, ereport, errcode(), errmsg(), ERROR, error_context_stack, SelectStmt::fromClause, SelectStmt::groupClause, SelectStmt::havingClause, ResTarget::indirection, initStringInfo(), SelectStmt::intoClause, IsA, SelectStmt::limitCount, SelectStmt::limitOffset, linitial, list_length(), SelectStmt::lockingClause, ResTarget::name, NIL, NULL, SelectStmt::op, pfree(), ErrorContextCallback::previous, raw_parser(), TypeName::setof, SelectStmt::sortClause, SelectStmt::targetList, TypeCast::typeName, typenameTypeIdAndMod(), ResTarget::val, SelectStmt::valuesLists, SelectStmt::whereClause, SelectStmt::windowClause, and SelectStmt::withClause.
Referenced by parseNameAndArgTypes(), plperl_spi_prepare(), pltcl_SPI_prepare(), PLy_spi_prepare(), and regtypein().
{
StringInfoData buf;
List *raw_parsetree_list;
SelectStmt *stmt;
ResTarget *restarget;
TypeCast *typecast;
TypeName *typeName;
ErrorContextCallback ptserrcontext;
/* make sure we give useful error for empty input */
if (strspn(str, " \t\n\r\f") == strlen(str))
goto fail;
initStringInfo(&buf);
appendStringInfo(&buf, "SELECT NULL::%s", str);
/*
* Setup error traceback support in case of ereport() during parse
*/
ptserrcontext.callback = pts_error_callback;
ptserrcontext.arg = (void *) str;
ptserrcontext.previous = error_context_stack;
error_context_stack = &ptserrcontext;
raw_parsetree_list = raw_parser(buf.data);
error_context_stack = ptserrcontext.previous;
/*
* Make sure we got back exactly what we expected and no more; paranoia is
* justified since the string might contain anything.
*/
if (list_length(raw_parsetree_list) != 1)
goto fail;
stmt = (SelectStmt *) linitial(raw_parsetree_list);
if (stmt == NULL ||
!IsA(stmt, SelectStmt) ||
stmt->distinctClause != NIL ||
stmt->intoClause != NULL ||
stmt->fromClause != NIL ||
stmt->whereClause != NULL ||
stmt->groupClause != NIL ||
stmt->havingClause != NULL ||
stmt->windowClause != NIL ||
stmt->valuesLists != NIL ||
stmt->sortClause != NIL ||
stmt->limitOffset != NULL ||
stmt->limitCount != NULL ||
stmt->lockingClause != NIL ||
stmt->withClause != NULL ||
stmt->op != SETOP_NONE)
goto fail;
if (list_length(stmt->targetList) != 1)
goto fail;
restarget = (ResTarget *) linitial(stmt->targetList);
if (restarget == NULL ||
!IsA(restarget, ResTarget) ||
restarget->name != NULL ||
restarget->indirection != NIL)
goto fail;
typecast = (TypeCast *) restarget->val;
if (typecast == NULL ||
!IsA(typecast, TypeCast) ||
typecast->arg == NULL ||
!IsA(typecast->arg, A_Const))
goto fail;
typeName = typecast->typeName;
if (typeName == NULL ||
!IsA(typeName, TypeName))
goto fail;
if (typeName->setof)
goto fail;
typenameTypeIdAndMod(NULL, typeName, typeid_p, typmod_p);
pfree(buf.data);
return;
fail:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid type name \"%s\"", str)));
}
| static void pts_error_callback | ( | void * | arg | ) | [static] |
Definition at line 644 of file parse_type.c.
References errcontext, and errposition().
{
const char *str = (const char *) arg;
errcontext("invalid type name \"%s\"", str);
/*
* Currently we just suppress any syntax error position report, rather
* than transforming to an "internal query" error. It's unlikely that a
* type name is complex enough to need positioning.
*/
errposition(0);
}
Definition at line 585 of file parse_type.c.
References datumIsEqual(), elog, GETSTRUCT, getTypeIOParam(), NameStr, OidInputFunctionCall(), and WARNING.
Referenced by coerce_type().
{
Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp);
Oid typinput = typform->typinput;
Oid typioparam = getTypeIOParam(tp);
Datum result;
result = OidInputFunctionCall(typinput, string,
typioparam, atttypmod);
#ifdef RANDOMIZE_ALLOCATED_MEMORY
/*
* For pass-by-reference data types, repeat the conversion to see if the
* input function leaves any uninitialized bytes in the result. We can
* only detect that reliably if RANDOMIZE_ALLOCATED_MEMORY is enabled, so
* we don't bother testing otherwise. The reason we don't want any
* instability in the input function is that comparison of Const nodes
* relies on bytewise comparison of the datums, so if the input function
* leaves garbage then subexpressions that should be identical may not get
* recognized as such. See pgsql-hackers discussion of 2008-04-04.
*/
if (string && !typform->typbyval)
{
Datum result2;
result2 = OidInputFunctionCall(typinput, string,
typioparam, atttypmod);
if (!datumIsEqual(result, result2, typform->typbyval, typform->typlen))
elog(WARNING, "type %s has unstable input conversion for \"%s\"",
NameStr(typform->typname), string);
}
#endif
return result;
}
Definition at line 540 of file parse_type.c.
References GETSTRUCT.
Referenced by coerce_type().
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
return typ->typbyval;
}
Definition at line 509 of file parse_type.c.
References elog, ERROR, HeapTupleIsValid, ObjectIdGetDatum, SearchSysCache1, and TYPEOID.
Referenced by coerce_type(), and find_typmod_coercion_function().
{
HeapTuple tup;
tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(id));
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for type %u", id);
return (Type) tup;
}
Definition at line 624 of file parse_type.c.
References elog, ERROR, GETSTRUCT, HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), SearchSysCache1, and TYPEOID.
Referenced by get_rte_attribute_is_dropped(), PLy_input_tuple_funcs(), PLy_output_tuple_funcs(), ProcedureCreate(), transformAssignmentIndirection(), typeInheritsFrom(), and typeIsOfTypedTable().
{
HeapTuple typeTuple;
Form_pg_type type;
Oid result;
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", type_id);
type = (Form_pg_type) GETSTRUCT(typeTuple);
result = type->typrelid;
ReleaseSysCache(typeTuple);
return result;
}
Definition at line 530 of file parse_type.c.
References GETSTRUCT.
Referenced by coerce_type().
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
return typ->typlen;
}
| char* TypeNameListToString | ( | List * | typenames | ) |
Definition at line 422 of file parse_type.c.
References appendStringInfoChar(), appendTypeNameToBuffer(), Assert, initStringInfo(), IsA, lfirst, and list_head().
Referenced by does_not_exist_skipping().
{
StringInfoData string;
ListCell *l;
initStringInfo(&string);
foreach(l, typenames)
{
TypeName *typeName = (TypeName *) lfirst(l);
Assert(IsA(typeName, TypeName));
if (l != list_head(typenames))
appendStringInfoChar(&string, ',');
appendTypeNameToBuffer(typeName, &string);
}
return string.data;
}
| char* TypeNameToString | ( | const TypeName * | typeName | ) |
Definition at line 408 of file parse_type.c.
References appendTypeNameToBuffer(), and initStringInfo().
Referenced by AlterDomainDropConstraint(), AlterDomainValidateConstraint(), AlterTypeOwner(), compute_return_type(), CreateCast(), defGetString(), defGetTypeLength(), DefineAggregate(), DefineDomain(), DefineOpClass(), does_not_exist_skipping(), examine_parameter_list(), get_object_address_type(), LookupTypeName(), LookupTypeNameOid(), MergeAttributes(), typenameType(), and typenameTypeMod().
{
StringInfoData string;
initStringInfo(&string);
appendTypeNameToBuffer(typeName, &string);
return string.data;
}
| Type typenameType | ( | ParseState * | pstate, | |
| const TypeName * | typeName, | |||
| int32 * | typmod_p | |||
| ) |
Definition at line 195 of file parse_type.c.
References ereport, errcode(), errmsg(), ERROR, GETSTRUCT, TypeName::location, LookupTypeName(), NULL, parser_errposition(), and TypeNameToString().
Referenced by ATExecAddColumn(), ATExecAddOf(), ATExecAlterColumnType(), DefineDomain(), DefineType(), transformColumnType(), transformOfType(), typenameTypeId(), and typenameTypeIdAndMod().
{
Type tup;
tup = LookupTypeName(pstate, typeName, typmod_p);
if (tup == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" does not exist",
TypeNameToString(typeName)),
parser_errposition(pstate, typeName->location)));
if (!((Form_pg_type) GETSTRUCT(tup))->typisdefined)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type \"%s\" is only a shell",
TypeNameToString(typeName)),
parser_errposition(pstate, typeName->location)));
return tup;
}
| Oid typenameTypeId | ( | ParseState * | pstate, | |
| const TypeName * | typeName | |||
| ) |
Definition at line 222 of file parse_type.c.
References HeapTupleGetOid, NULL, ReleaseSysCache(), and typenameType().
Referenced by AlterDomainAddConstraint(), AlterDomainDefault(), AlterDomainDropConstraint(), AlterDomainNotNull(), AlterDomainValidateConstraint(), AlterEnum(), AlterTypeNamespace(), check_object_ownership(), CreateCast(), DefineAggregate(), DefineOpClass(), DefineOperator(), DefineRange(), DefineRelation(), DefineType(), does_not_exist_skipping(), get_object_address(), LookupOperNameTypeNames(), objectNamesToOids(), PrepareQuery(), processTypesSpec(), RenameConstraint(), RenameType(), and transformAExprOf().
{
Oid typoid;
Type tup;
tup = typenameType(pstate, typeName, NULL);
typoid = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
return typoid;
}
| void typenameTypeIdAndMod | ( | ParseState * | pstate, | |
| const TypeName * | typeName, | |||
| Oid * | typeid_p, | |||
| int32 * | typmod_p | |||
| ) |
Definition at line 241 of file parse_type.c.
References HeapTupleGetOid, ReleaseSysCache(), and typenameType().
Referenced by addRangeTableEntryForFunction(), ATExecAddColumn(), ATPrepAlterColumnType(), BuildDescForRelation(), flatten_set_variable_args(), MergeAttributes(), parseTypeString(), transformExprRecurse(), transformTypeCast(), and transformXmlSerialize().
{
Type tup;
tup = typenameType(pstate, typeName, typmod_p);
*typeid_p = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
}
| static int32 typenameTypeMod | ( | ParseState * | pstate, | |
| const TypeName * | typeName, | |||
| Type | typ | |||
| ) | [static] |
Definition at line 263 of file parse_type.c.
References cancel_parser_errposition_callback(), construct_array(), CStringGetDatum, CSTRINGOID, DatumGetInt32, ereport, errcode(), errmsg(), ERROR, ColumnRef::fields, GETSTRUCT, InvalidOid, IsA, Value::ValUnion::ival, lfirst, linitial, list_length(), TypeName::location, NIL, OidFunctionCall1, palloc(), parser_errposition(), pfree(), PointerGetDatum, setup_parser_errposition_callback(), snprintf(), Value::ValUnion::str, strVal, tm, TypeName::typemod, TypeNameToString(), TypeName::typmods, Value::val, and A_Const::val.
Referenced by LookupTypeName().
{
int32 result;
Oid typmodin;
Datum *datums;
int n;
ListCell *l;
ArrayType *arrtypmod;
ParseCallbackState pcbstate;
/* Return prespecified typmod if no typmod expressions */
if (typeName->typmods == NIL)
return typeName->typemod;
/*
* Else, type had better accept typmods. We give a special error message
* for the shell-type case, since a shell couldn't possibly have a
* typmodin function.
*/
if (!((Form_pg_type) GETSTRUCT(typ))->typisdefined)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type modifier cannot be specified for shell type \"%s\"",
TypeNameToString(typeName)),
parser_errposition(pstate, typeName->location)));
typmodin = ((Form_pg_type) GETSTRUCT(typ))->typmodin;
if (typmodin == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type modifier is not allowed for type \"%s\"",
TypeNameToString(typeName)),
parser_errposition(pstate, typeName->location)));
/*
* Convert the list of raw-grammar-output expressions to a cstring array.
* Currently, we allow simple numeric constants, string literals, and
* identifiers; possibly this list could be extended.
*/
datums = (Datum *) palloc(list_length(typeName->typmods) * sizeof(Datum));
n = 0;
foreach(l, typeName->typmods)
{
Node *tm = (Node *) lfirst(l);
char *cstr = NULL;
if (IsA(tm, A_Const))
{
A_Const *ac = (A_Const *) tm;
if (IsA(&ac->val, Integer))
{
cstr = (char *) palloc(32);
snprintf(cstr, 32, "%ld", (long) ac->val.val.ival);
}
else if (IsA(&ac->val, Float) ||
IsA(&ac->val, String))
{
/* we can just use the str field directly. */
cstr = ac->val.val.str;
}
}
else if (IsA(tm, ColumnRef))
{
ColumnRef *cr = (ColumnRef *) tm;
if (list_length(cr->fields) == 1 &&
IsA(linitial(cr->fields), String))
cstr = strVal(linitial(cr->fields));
}
if (!cstr)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type modifiers must be simple constants or identifiers"),
parser_errposition(pstate, typeName->location)));
datums[n++] = CStringGetDatum(cstr);
}
/* hardwired knowledge about cstring's representation details here */
arrtypmod = construct_array(datums, n, CSTRINGOID,
-2, false, 'c');
/* arrange to report location if type's typmodin function fails */
setup_parser_errposition_callback(&pcbstate, pstate, typeName->location);
result = DatumGetInt32(OidFunctionCall1(typmodin,
PointerGetDatum(arrtypmod)));
cancel_parser_errposition_callback(&pcbstate);
pfree(datums);
pfree(arrtypmod);
return result;
}
Definition at line 571 of file parse_type.c.
References GETSTRUCT.
Referenced by coerce_type().
{
Form_pg_type typtup;
typtup = (Form_pg_type) GETSTRUCT(typ);
return typtup->typcollation;
}
Definition at line 521 of file parse_type.c.
References elog, ERROR, HeapTupleGetOid, and NULL.
Referenced by AlterTypeOwner(), compute_return_type(), examine_parameter_list(), FuncNameAsType(), get_object_address_type(), and LookupTypeNameOid().
{
if (tp == NULL) /* probably useless */
elog(ERROR, "typeTypeId() called with NULL type struct");
return HeapTupleGetOid(tp);
}
| char* typeTypeName | ( | Type | t | ) |
Definition at line 550 of file parse_type.c.
References GETSTRUCT, NameStr, and pstrdup().
{
Form_pg_type typ;
typ = (Form_pg_type) GETSTRUCT(t);
/* pstrdup here because result may need to outlive the syscache entry */
return pstrdup(NameStr(typ->typname));
}
Definition at line 561 of file parse_type.c.
References GETSTRUCT.
Referenced by FuncNameAsType().
{
Form_pg_type typtup;
typtup = (Form_pg_type) GETSTRUCT(typ);
return typtup->typrelid;
}
1.7.1