1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-26 01:22:12 +03:00

Support type modifiers for user-defined types, and pull most knowledge

about typmod representation for standard types out into type-specific
typmod I/O functions.  Teodor Sigaev, with some editorialization by
Tom Lane.
This commit is contained in:
Tom Lane
2006-12-30 21:21:56 +00:00
parent 24b1f14eae
commit 5725b9d9af
47 changed files with 1628 additions and 685 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.207 2006/12/23 00:43:09 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.208 2006/12/30 21:21:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -889,6 +889,9 @@ MergeAttributes(List *schema, List *supers, bool istemp,
exist_attno = findAttrByName(attributeName, inhSchema);
if (exist_attno > 0)
{
Oid defTypeId;
int32 deftypmod;
/*
* Yes, try to merge the two column definitions. They must
* have the same type and typmod.
@ -897,8 +900,10 @@ MergeAttributes(List *schema, List *supers, bool istemp,
(errmsg("merging multiple inherited definitions of column \"%s\"",
attributeName)));
def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
if (typenameTypeId(NULL, def->typename) != attribute->atttypid ||
def->typename->typmod != attribute->atttypmod)
defTypeId = typenameTypeId(NULL, def->typename);
deftypmod = typenameTypeMod(NULL, def->typename, defTypeId);
if (defTypeId != attribute->atttypid ||
deftypmod != attribute->atttypmod)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("inherited column \"%s\" has a type conflict",
@ -1029,6 +1034,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
if (exist_attno > 0)
{
ColumnDef *def;
Oid defTypeId, newTypeId;
int32 deftypmod, newtypmod;
/*
* Yes, try to merge the two column definitions. They must
@ -1038,8 +1045,11 @@ MergeAttributes(List *schema, List *supers, bool istemp,
(errmsg("merging column \"%s\" with inherited definition",
attributeName)));
def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
if (typenameTypeId(NULL, def->typename) != typenameTypeId(NULL, newdef->typename) ||
def->typename->typmod != newdef->typename->typmod)
defTypeId = typenameTypeId(NULL, def->typename);
deftypmod = typenameTypeMod(NULL, def->typename, defTypeId);
newTypeId = typenameTypeId(NULL, newdef->typename);
newtypmod = typenameTypeMod(NULL, newdef->typename, newTypeId);
if (defTypeId != newTypeId || deftypmod != newtypmod)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" has a type conflict",
@ -3092,6 +3102,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
maxatts;
HeapTuple typeTuple;
Oid typeOid;
int32 typmod;
Form_pg_type tform;
Expr *defval;
@ -3110,10 +3121,14 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
if (HeapTupleIsValid(tuple))
{
Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
Oid ctypeId;
int32 ctypmod;
/* Okay if child matches by type */
if (typenameTypeId(NULL, colDef->typename) != childatt->atttypid ||
colDef->typename->typmod != childatt->atttypmod)
ctypeId = typenameTypeId(NULL, colDef->typename);
ctypmod = typenameTypeMod(NULL, colDef->typename, ctypeId);
if (ctypeId != childatt->atttypid ||
ctypmod != childatt->atttypmod)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("child table \"%s\" has different type for column \"%s\"",
@ -3169,6 +3184,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
typeTuple = typenameType(NULL, colDef->typename);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
typeOid = HeapTupleGetOid(typeTuple);
typmod = typenameTypeMod(NULL, colDef->typename, typeOid);
/* make sure datatype is legal for a column */
CheckAttributeType(colDef->colname, typeOid);
@ -3186,7 +3202,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
attribute->attstattarget = -1;
attribute->attlen = tform->typlen;
attribute->attcacheoff = -1;
attribute->atttypmod = colDef->typename->typmod;
attribute->atttypmod = typmod;
attribute->attnum = i;
attribute->attbyval = tform->typbyval;
attribute->attndims = list_length(colDef->typename->arrayBounds);
@ -3278,7 +3294,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
(Node *) defval,
basetype,
typeOid,
colDef->typename->typmod,
typmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST);
if (defval == NULL) /* should not happen */
@ -4877,6 +4893,7 @@ ATPrepAlterColumnType(List **wqueue,
Form_pg_attribute attTup;
AttrNumber attnum;
Oid targettype;
int32 targettypmod;
Node *transform;
NewColumnValue *newval;
ParseState *pstate = make_parsestate(NULL);
@ -4907,6 +4924,7 @@ ATPrepAlterColumnType(List **wqueue,
/* Look up the target type */
targettype = typenameTypeId(NULL, typename);
targettypmod = typenameTypeMod(NULL, typename, targettype);
/* make sure datatype is legal for a column */
CheckAttributeType(colName, targettype);
@ -4958,7 +4976,7 @@ ATPrepAlterColumnType(List **wqueue,
transform = coerce_to_target_type(pstate,
transform, exprType(transform),
targettype, typename->typmod,
targettype, targettypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST);
if (transform == NULL)
@ -5004,6 +5022,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
HeapTuple typeTuple;
Form_pg_type tform;
Oid targettype;
int32 targettypmod;
Node *defaultexpr;
Relation attrelation;
Relation depRel;
@ -5035,6 +5054,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
typeTuple = typenameType(NULL, typename);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
targettype = HeapTupleGetOid(typeTuple);
targettypmod = typenameTypeMod(NULL, typename, targettype);
/*
* If there is a default expression for the column, get it and ensure we
@ -5055,7 +5075,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
defaultexpr = strip_implicit_coercions(defaultexpr);
defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
defaultexpr, exprType(defaultexpr),
targettype, typename->typmod,
targettype, targettypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST);
if (defaultexpr == NULL)
@ -5272,7 +5292,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
* copy of the syscache entry, so okay to scribble on.)
*/
attTup->atttypid = targettype;
attTup->atttypmod = typename->typmod;
attTup->atttypmod = targettypmod;
attTup->attndims = list_length(typename->arrayBounds);
attTup->attlen = tform->typlen;
attTup->attbyval = tform->typbyval;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.97 2006/10/04 00:29:51 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.98 2006/12/30 21:21:53 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -75,6 +75,8 @@ static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkDomainOwner(HeapTuple tup, TypeName *typename);
@ -100,6 +102,8 @@ DefineType(List *names, List *parameters)
List *outputName = NIL;
List *receiveName = NIL;
List *sendName = NIL;
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
char *defaultValue = NULL;
bool byValue = false;
@ -110,6 +114,8 @@ DefineType(List *names, List *parameters)
Oid outputOid;
Oid receiveOid = InvalidOid;
Oid sendOid = InvalidOid;
Oid typmodinOid = InvalidOid;
Oid typmodoutOid = InvalidOid;
Oid analyzeOid = InvalidOid;
char *shadow_type;
ListCell *pl;
@ -182,6 +188,10 @@ DefineType(List *names, List *parameters)
receiveName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "send") == 0)
sendName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
typmodinName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
typmodoutName = defGetQualifiedName(defel);
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
analyzeName = defGetQualifiedName(defel);
@ -268,6 +278,11 @@ DefineType(List *names, List *parameters)
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("type output function must be specified")));
if (typmodinName == NIL && typmodoutName != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("type modifier output function is useless without a type modifier input function")));
/*
* Convert I/O proc names to OIDs
*/
@ -335,6 +350,14 @@ DefineType(List *names, List *parameters)
NameListToString(sendName))));
}
/*
* Convert typmodin/out function proc names to OIDs.
*/
if (typmodinName)
typmodinOid = findTypeTypmodinFunction(typmodinName);
if (typmodoutName)
typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
/*
* Convert analysis function proc name to an OID. If no analysis function
* is specified, we'll use zero to select the built-in default algorithm.
@ -362,6 +385,12 @@ DefineType(List *names, List *parameters)
if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(sendName));
if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(typmodinName));
if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(typmodoutName));
if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(analyzeName));
@ -381,6 +410,8 @@ DefineType(List *names, List *parameters)
outputOid, /* output procedure */
receiveOid, /* receive procedure */
sendOid, /* send procedure */
typmodinOid, /* typmodin procedure */
typmodoutOid,/* typmodout procedure */
analyzeOid, /* analyze procedure */
elemType, /* element type ID */
InvalidOid, /* base type ID (only for domains) */
@ -413,6 +444,8 @@ DefineType(List *names, List *parameters)
F_ARRAY_OUT, /* output procedure */
F_ARRAY_RECV, /* receive procedure */
F_ARRAY_SEND, /* send procedure */
typmodinOid, /* typmodin procedure */
typmodoutOid, /* typmodout procedure */
InvalidOid, /* analyze procedure - default */
typoid, /* element type ID */
InvalidOid, /* base type ID */
@ -552,6 +585,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid basetypeoid;
Oid domainoid;
Form_pg_type baseType;
int32 basetypeMod;
/* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@ -581,9 +615,9 @@ DefineDomain(CreateDomainStmt *stmt)
* Look up the base type.
*/
typeTup = typenameType(NULL, stmt->typename);
baseType = (Form_pg_type) GETSTRUCT(typeTup);
basetypeoid = HeapTupleGetOid(typeTup);
basetypeMod = typenameTypeMod(NULL, stmt->typename, basetypeoid);
/*
* Base type must be a plain base type or another domain. Domains over
@ -621,6 +655,8 @@ DefineDomain(CreateDomainStmt *stmt)
receiveProcedure = F_DOMAIN_RECV;
sendProcedure = baseType->typsend;
/* Domains never accept typmods, so no typmodin/typmodout needed */
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
@ -681,7 +717,7 @@ DefineDomain(CreateDomainStmt *stmt)
*/
defaultExpr = cookDefault(pstate, constr->raw_expr,
basetypeoid,
stmt->typename->typmod,
basetypeMod,
domainName);
/*
@ -768,6 +804,8 @@ DefineDomain(CreateDomainStmt *stmt)
outputProcedure, /* output procedure */
receiveProcedure, /* receive procedure */
sendProcedure, /* send procedure */
InvalidOid, /* typmodin procedure - none */
InvalidOid, /* typmodout procedure - none */
analyzeProcedure, /* analyze procedure */
typelem, /* element type ID */
basetypeoid, /* base type ID */
@ -776,7 +814,7 @@ DefineDomain(CreateDomainStmt *stmt)
byValue, /* passed by value */
alignment, /* required alignment */
storage, /* TOAST strategy */
stmt->typename->typmod, /* typeMod value */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull); /* Type NOT NULL */
@ -793,7 +831,7 @@ DefineDomain(CreateDomainStmt *stmt)
{
case CONSTR_CHECK:
domainAddConstraint(domainoid, domainNamespace,
basetypeoid, stmt->typename->typmod,
basetypeoid, basetypeMod,
constr, domainName);
break;
@ -1067,6 +1105,60 @@ findTypeSendFunction(List *procname, Oid typeOid)
return InvalidOid; /* keep compiler quiet */
}
static Oid
findTypeTypmodinFunction(List *procname)
{
Oid argList[1];
Oid procOid;
/*
* typmodin functions always take one int4[] argument and return int4.
*/
argList[0] = INT4ARRAYOID;
procOid = LookupFuncName(procname, 1, argList, true);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
if (get_func_rettype(procOid) != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("typmod_in function %s must return type \"integer\"",
NameListToString(procname))));
return procOid;
}
static Oid
findTypeTypmodoutFunction(List *procname)
{
Oid argList[1];
Oid procOid;
/*
* typmodout functions always take one int4 argument and return cstring.
*/
argList[0] = INT4OID;
procOid = LookupFuncName(procname, 1, argList, true);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 1, argList))));
if (get_func_rettype(procOid) != CSTRINGOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("typmod_out function %s must return type \"cstring\"",
NameListToString(procname))));
return procOid;
}
static Oid
findTypeAnalyzeFunction(List *procname, Oid typeOid)
{
@ -1244,6 +1336,8 @@ AlterDomainDefault(List *names, Node *defaultRaw)
typTup->typoutput,
typTup->typreceive,
typTup->typsend,
typTup->typmodin,
typTup->typmodout,
typTup->typanalyze,
typTup->typelem,
typTup->typbasetype,