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:
@ -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;
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user