mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Code review for DOMAIN patch.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.188 2002/03/19 02:58:19 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.189 2002/03/20 19:43:34 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@@ -705,7 +705,7 @@ AddNewRelationType(char *typeName, Oid new_rel_oid, Oid new_type_oid)
|
||||
true, /* passed by value */
|
||||
'i', /* default alignment - same as for OID */
|
||||
'p', /* Not TOASTable */
|
||||
-1, /* Type mod length */
|
||||
-1, /* typmod */
|
||||
0, /* array dimensions for typBaseType */
|
||||
false); /* Type NOT NULL */
|
||||
}
|
||||
@@ -1589,10 +1589,7 @@ AddRelationRawConstraints(Relation rel,
|
||||
RangeTblEntry *rte;
|
||||
int numchecks;
|
||||
List *listptr;
|
||||
|
||||
/* Probably shouldn't be null by default */
|
||||
Node *expr = NULL;
|
||||
|
||||
Node *expr;
|
||||
|
||||
/*
|
||||
* Get info about existing constraints.
|
||||
@@ -1624,17 +1621,11 @@ AddRelationRawConstraints(Relation rel,
|
||||
foreach(listptr, rawColDefaults)
|
||||
{
|
||||
RawColumnDefault *colDef = (RawColumnDefault *) lfirst(listptr);
|
||||
|
||||
|
||||
Form_pg_attribute atp = rel->rd_att->attrs[colDef->attnum - 1];
|
||||
|
||||
expr = cookDefault(pstate, colDef->raw_default
|
||||
, atp->atttypid, atp->atttypmod
|
||||
, NameStr(atp->attname));
|
||||
|
||||
/*
|
||||
* OK, store it.
|
||||
*/
|
||||
expr = cookDefault(pstate, colDef->raw_default,
|
||||
atp->atttypid, atp->atttypmod,
|
||||
NameStr(atp->attname));
|
||||
StoreAttrDefault(rel, colDef->attnum, nodeToString(expr));
|
||||
}
|
||||
|
||||
@@ -1646,7 +1637,6 @@ AddRelationRawConstraints(Relation rel,
|
||||
{
|
||||
Constraint *cdef = (Constraint *) lfirst(listptr);
|
||||
char *ccname;
|
||||
Node *expr;
|
||||
|
||||
if (cdef->contype != CONSTR_CHECK || cdef->raw_expr == NULL)
|
||||
continue;
|
||||
@@ -1851,17 +1841,21 @@ SetRelationNumChecks(Relation rel, int numchecks)
|
||||
* Take a raw default and convert it to a cooked format ready for
|
||||
* storage.
|
||||
*
|
||||
* Parse state, attypid, attypmod and attname are required for
|
||||
* CoerceTargetExpr() and more importantly transformExpr().
|
||||
* Parse state should be set up to recognize any vars that might appear
|
||||
* in the expression. (Even though we plan to reject vars, it's more
|
||||
* user-friendly to give the correct error message than "unknown var".)
|
||||
*
|
||||
* If atttypid is not InvalidOid, check that the expression is coercible
|
||||
* to the specified type. atttypmod is needed in this case, and attname
|
||||
* is used in the error message if any.
|
||||
*/
|
||||
Node *
|
||||
cookDefault(ParseState *pstate,
|
||||
Node *raw_default,
|
||||
Oid atttypid,
|
||||
int32 atttypmod,
|
||||
char *attname) {
|
||||
|
||||
Oid type_id;
|
||||
char *attname)
|
||||
{
|
||||
Node *expr;
|
||||
|
||||
Assert(raw_default != NULL);
|
||||
@@ -1896,22 +1890,20 @@ cookDefault(ParseState *pstate,
|
||||
* will actually do the coercion, to ensure we don't accept an
|
||||
* unusable default expression.
|
||||
*/
|
||||
type_id = exprType(expr);
|
||||
if (type_id != InvalidOid && atttypid != InvalidOid) {
|
||||
if (type_id != atttypid) {
|
||||
if (OidIsValid(atttypid))
|
||||
{
|
||||
Oid type_id = exprType(expr);
|
||||
|
||||
/* Try coercing to the base type of the domain if available */
|
||||
if (type_id != atttypid)
|
||||
{
|
||||
if (CoerceTargetExpr(pstate, expr, type_id,
|
||||
getBaseType(atttypid),
|
||||
atttypmod) == NULL) {
|
||||
|
||||
atttypid, atttypmod) == NULL)
|
||||
elog(ERROR, "Column \"%s\" is of type %s"
|
||||
" but default expression is of type %s"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
" but default expression is of type %s"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
attname,
|
||||
format_type_be(atttypid),
|
||||
format_type_be(type_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.40 2001/10/25 05:49:23 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.41 2002/03/20 19:43:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -135,14 +135,13 @@ AggregateCreate(char *aggName,
|
||||
|
||||
/*
|
||||
* If the transfn is strict and the initval is NULL, make sure input
|
||||
* type and transtype are the same (or at least binary- compatible),
|
||||
* type and transtype are the same (or at least binary-compatible),
|
||||
* so that it's OK to use the first input value as the initial
|
||||
* transValue.
|
||||
*/
|
||||
if (proc->proisstrict && agginitval == NULL)
|
||||
{
|
||||
if (basetype != transtype &&
|
||||
!IS_BINARY_COMPATIBLE(basetype, transtype))
|
||||
if (!IsBinaryCompatible(basetype, transtype))
|
||||
elog(ERROR, "must not omit initval when transfn is strict and transtype is not compatible with input type");
|
||||
}
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.65 2002/03/06 06:09:26 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.66 2002/03/20 19:43:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -423,7 +423,7 @@ checkretval(Oid rettype, List *queryTreeList)
|
||||
format_type_be(rettype));
|
||||
|
||||
restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
|
||||
if (restype != rettype && !IS_BINARY_COMPATIBLE(restype, rettype))
|
||||
if (!IsBinaryCompatible(restype, rettype))
|
||||
elog(ERROR, "return type mismatch in function: declared to return %s, returns %s",
|
||||
format_type_be(rettype), format_type_be(restype));
|
||||
|
||||
@@ -440,7 +440,7 @@ checkretval(Oid rettype, List *queryTreeList)
|
||||
if (tlistlen == 1)
|
||||
{
|
||||
restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
|
||||
if (restype == rettype || IS_BINARY_COMPATIBLE(restype, rettype))
|
||||
if (IsBinaryCompatible(restype, rettype))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ checkretval(Oid rettype, List *queryTreeList)
|
||||
continue;
|
||||
tletype = exprType(tle->expr);
|
||||
atttype = reln->rd_att->attrs[i]->atttypid;
|
||||
if (tletype != atttype && !IS_BINARY_COMPATIBLE(tletype, atttype))
|
||||
if (!IsBinaryCompatible(tletype, atttype))
|
||||
elog(ERROR, "function declared to return %s returns %s instead of %s at column %d",
|
||||
format_type_be(rettype),
|
||||
format_type_be(tletype),
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.68 2002/03/19 02:18:14 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.69 2002/03/20 19:43:38 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -179,13 +179,11 @@ TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName)
|
||||
values[i++] = CharGetDatum('i'); /* 15 */
|
||||
values[i++] = CharGetDatum('p'); /* 16 */
|
||||
values[i++] = BoolGetDatum(false); /* 17 */
|
||||
values[i++] = Int32GetDatum(-1); /* 18 */
|
||||
values[i++] = ObjectIdGetDatum(InvalidOid); /* 19 */
|
||||
values[i++] = ObjectIdGetDatum(InvalidOid); /* 18 */
|
||||
values[i++] = Int32GetDatum(-1); /* 19 */
|
||||
values[i++] = Int32GetDatum(0); /* 20 */
|
||||
values[i++] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(typeName)); /* 21 */
|
||||
values[i++] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(typeName)); /* 22 */
|
||||
nulls[i++] = 'n'; /* 21 */
|
||||
nulls[i++] = 'n'; /* 22 */
|
||||
|
||||
/*
|
||||
* create a new type tuple with FormHeapTuple
|
||||
@@ -288,7 +286,7 @@ TypeCreate(char *typeName,
|
||||
char storage,
|
||||
int32 typeMod,
|
||||
int32 typNDims, /* Array dimensions for baseTypeName */
|
||||
bool typeNotNull) /* binary default representation (cooked) */
|
||||
bool typeNotNull)
|
||||
{
|
||||
int i,
|
||||
j;
|
||||
@@ -452,21 +450,10 @@ TypeCreate(char *typeName,
|
||||
*/
|
||||
values[i++] = CharGetDatum(storage); /* 16 */
|
||||
|
||||
/*
|
||||
* set the typenotnull value
|
||||
*/
|
||||
values[i++] = BoolGetDatum(typeNotNull); /* 17 */
|
||||
|
||||
/*
|
||||
* set the typemod value
|
||||
*/
|
||||
values[i++] = Int32GetDatum(typeMod); /* 18 */
|
||||
|
||||
values[i++] = ObjectIdGetDatum(baseObjectId); /* 19 */
|
||||
|
||||
/*
|
||||
* Dimension number for an array base type
|
||||
*/
|
||||
/* 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 */
|
||||
|
||||
/*
|
||||
@@ -475,17 +462,17 @@ TypeCreate(char *typeName,
|
||||
*/
|
||||
if (defaultTypeBin)
|
||||
values[i] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(defaultTypeBin));
|
||||
CStringGetDatum(defaultTypeBin));
|
||||
else
|
||||
nulls[i] = 'n';
|
||||
i++; /* 21 */
|
||||
i++; /* 21 */
|
||||
|
||||
/*
|
||||
* initialize the default value for this type.
|
||||
*/
|
||||
if (defaultTypeValue)
|
||||
values[i] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(defaultTypeValue));
|
||||
CStringGetDatum(defaultTypeValue));
|
||||
else
|
||||
nulls[i] = 'n';
|
||||
i++; /* 22 */
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.87 2002/03/19 02:58:19 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.88 2002/03/20 19:43:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -42,6 +42,7 @@ static int findAttrByName(const char *attributeName, List *schema);
|
||||
static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
|
||||
static List *MergeDomainAttributes(List *schema);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* DefineRelation
|
||||
* Creates a new relation.
|
||||
@@ -71,9 +72,9 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
StrNCpy(relname, stmt->relname, NAMEDATALEN);
|
||||
|
||||
/*
|
||||
* Inherit domain attributes into the known columns before table inheritance
|
||||
* applies it's changes otherwise we risk adding double constraints
|
||||
* to a domain thats inherited.
|
||||
* Merge domain attributes into the known columns before processing table
|
||||
* inheritance. Otherwise we risk adding double constraints to a
|
||||
* domain-type column that's inherited.
|
||||
*/
|
||||
schema = MergeDomainAttributes(schema);
|
||||
|
||||
@@ -273,11 +274,8 @@ TruncateRelation(const char *relname)
|
||||
/*
|
||||
* MergeDomainAttributes
|
||||
* Returns a new table schema with the constraints, types, and other
|
||||
* attributes of the domain resolved for fields using the domain as
|
||||
* their type.
|
||||
*
|
||||
* Defaults are pulled out by the table attribute as required, similar to
|
||||
* how all types defaults are processed.
|
||||
* attributes of domains resolved for fields using a domain as
|
||||
* their type.
|
||||
*/
|
||||
static List *
|
||||
MergeDomainAttributes(List *schema)
|
||||
@@ -295,34 +293,25 @@ MergeDomainAttributes(List *schema)
|
||||
HeapTuple tuple;
|
||||
Form_pg_type typeTup;
|
||||
|
||||
|
||||
tuple = SearchSysCache(TYPENAME,
|
||||
CStringGetDatum(coldef->typename->name),
|
||||
0,0,0);
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "MergeDomainAttributes: Type %s does not exist",
|
||||
coldef->typename->name);
|
||||
|
||||
typeTup = (Form_pg_type) GETSTRUCT(tuple);
|
||||
if (typeTup->typtype == 'd') {
|
||||
/*
|
||||
* This is a domain, lets force the properties of the domain on to
|
||||
* the new column.
|
||||
*/
|
||||
|
||||
/* Enforce the typmod value */
|
||||
coldef->typename->typmod = typeTup->typmod;
|
||||
|
||||
/* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
|
||||
coldef->is_not_null |= typeTup->typnotnull;
|
||||
|
||||
/* Enforce the element type in the event the domain is an array
|
||||
*
|
||||
* BUG: How do we fill out arrayBounds and attrname from typelem and typNDimms?
|
||||
*/
|
||||
|
||||
if (typeTup->typtype == 'd')
|
||||
{
|
||||
/* Force the column to have the correct typmod. */
|
||||
coldef->typename->typmod = typeTup->typtypmod;
|
||||
/* XXX more to do here? */
|
||||
}
|
||||
|
||||
/* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
|
||||
/* Currently only used for domains, but could be valid for all */
|
||||
coldef->is_not_null |= typeTup->typnotnull;
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.70 2002/03/19 02:18:15 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.71 2002/03/20 19:43:44 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -483,38 +483,29 @@ DefineAggregate(char *aggName, List *parameters)
|
||||
void
|
||||
DefineDomain(CreateDomainStmt *stmt)
|
||||
{
|
||||
int16 internalLength = -1; /* int2 */
|
||||
int16 externalLength = -1; /* int2 */
|
||||
char *inputName = NULL;
|
||||
char *outputName = NULL;
|
||||
char *sendName = NULL;
|
||||
char *receiveName = NULL;
|
||||
|
||||
/*
|
||||
* Domains store the external representation in defaultValue
|
||||
* and the interal Node representation in defaultValueBin
|
||||
*/
|
||||
char *defaultValue = NULL;
|
||||
char *defaultValueBin = NULL;
|
||||
|
||||
bool byValue = false;
|
||||
char delimiter = DEFAULT_TYPDELIM;
|
||||
char alignment = 'i'; /* default alignment */
|
||||
char storage = 'p'; /* default TOAST storage method */
|
||||
int16 internalLength;
|
||||
int16 externalLength;
|
||||
char *inputName;
|
||||
char *outputName;
|
||||
char *sendName;
|
||||
char *receiveName;
|
||||
bool byValue;
|
||||
char delimiter;
|
||||
char alignment;
|
||||
char storage;
|
||||
char typtype;
|
||||
Datum datum;
|
||||
bool isnull;
|
||||
char *defaultValue = NULL;
|
||||
char *defaultValueBin = NULL;
|
||||
bool typNotNull = false;
|
||||
Oid basetypelem;
|
||||
char *elemName = NULL;
|
||||
int32 typNDims = 0; /* No array dimensions by default */
|
||||
|
||||
bool isnull;
|
||||
Relation pg_type_rel;
|
||||
TupleDesc pg_type_dsc;
|
||||
HeapTuple typeTup;
|
||||
char *typeName = stmt->typename->name;
|
||||
|
||||
List *listptr;
|
||||
List *schema = stmt->constraints;
|
||||
List *listptr;
|
||||
|
||||
/*
|
||||
* Domainnames, unlike typenames don't need to account for the '_'
|
||||
@@ -524,25 +515,13 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
|
||||
NAMEDATALEN - 1);
|
||||
|
||||
|
||||
/* Test for existing Domain (or type) of that name */
|
||||
typeTup = SearchSysCache( TYPENAME
|
||||
, PointerGetDatum(stmt->domainname)
|
||||
, 0, 0, 0
|
||||
);
|
||||
|
||||
typeTup = SearchSysCache(TYPENAME,
|
||||
PointerGetDatum(stmt->domainname),
|
||||
0, 0, 0);
|
||||
if (HeapTupleIsValid(typeTup))
|
||||
{
|
||||
elog(ERROR, "CREATE DOMAIN: domain or type %s already exists",
|
||||
elog(ERROR, "CREATE DOMAIN: domain or type %s already exists",
|
||||
stmt->domainname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the information about old types
|
||||
*/
|
||||
pg_type_rel = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||
pg_type_dsc = RelationGetDescr(pg_type_rel);
|
||||
|
||||
|
||||
/*
|
||||
* When the type is an array for some reason we don't actually receive
|
||||
@@ -555,22 +534,12 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
typNDims = length(stmt->typename->arrayBounds);
|
||||
}
|
||||
|
||||
|
||||
typeTup = SearchSysCache( TYPENAME
|
||||
, PointerGetDatum(typeName)
|
||||
, 0, 0, 0
|
||||
);
|
||||
|
||||
typeTup = SearchSysCache(TYPENAME,
|
||||
PointerGetDatum(typeName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(typeTup))
|
||||
{
|
||||
elog(ERROR, "CREATE DOMAIN: type %s does not exist",
|
||||
stmt->typename->name);
|
||||
}
|
||||
|
||||
|
||||
/* Check that this is a basetype */
|
||||
typtype = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typtype, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/*
|
||||
* What we really don't want is domains of domains. This could cause all sorts
|
||||
@@ -578,9 +547,10 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
*
|
||||
* With testing, we may determine complex types should be allowed
|
||||
*/
|
||||
if (typtype != 'b') {
|
||||
elog(ERROR, "DefineDomain: %s is not a basetype", stmt->typename->name);
|
||||
}
|
||||
typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
|
||||
if (typtype != 'b')
|
||||
elog(ERROR, "DefineDomain: %s is not a basetype",
|
||||
stmt->typename->name);
|
||||
|
||||
/* passed by value */
|
||||
byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
|
||||
@@ -588,6 +558,9 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
/* Required Alignment */
|
||||
alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
|
||||
|
||||
/* TOAST Strategy */
|
||||
storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
|
||||
|
||||
/* Storage Length */
|
||||
internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
|
||||
|
||||
@@ -597,70 +570,66 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
/* Array element Delimiter */
|
||||
delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
|
||||
|
||||
/*
|
||||
* XXX this is pretty bogus: should be passing function OIDs to
|
||||
* TypeCreate, not names which aren't unique.
|
||||
*/
|
||||
|
||||
/* Input Function Name */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typinput, pg_type_dsc, &isnull);
|
||||
datum = SysCacheGetAttr(TYPENAME, typeTup, Anum_pg_type_typinput, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
inputName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* Output Function Name */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typoutput, pg_type_dsc, &isnull);
|
||||
datum = SysCacheGetAttr(TYPENAME, typeTup, Anum_pg_type_typoutput, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
outputName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* ReceiveName */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typreceive, pg_type_dsc, &isnull);
|
||||
datum = SysCacheGetAttr(TYPENAME, typeTup, Anum_pg_type_typreceive, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
receiveName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* SendName */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typsend, pg_type_dsc, &isnull);
|
||||
datum = SysCacheGetAttr(TYPENAME, typeTup, Anum_pg_type_typsend, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
sendName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* TOAST Strategy */
|
||||
storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
|
||||
Assert(!isnull);
|
||||
|
||||
/* Inherited default value */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typdefault, pg_type_dsc, &isnull);
|
||||
if (!isnull) {
|
||||
defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
|
||||
}
|
||||
datum = SysCacheGetAttr(TYPENAME, typeTup,
|
||||
Anum_pg_type_typdefault, &isnull);
|
||||
if (!isnull)
|
||||
defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
|
||||
|
||||
/* Inherited default binary value */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typdefaultbin, pg_type_dsc, &isnull);
|
||||
if (!isnull) {
|
||||
defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
|
||||
}
|
||||
datum = SysCacheGetAttr(TYPENAME, typeTup,
|
||||
Anum_pg_type_typdefaultbin, &isnull);
|
||||
if (!isnull)
|
||||
defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
|
||||
|
||||
/*
|
||||
* Pull out the typelem name of the parent OID.
|
||||
*
|
||||
* This is what enables us to make a domain of an array
|
||||
*/
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typelem, pg_type_dsc, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
if (DatumGetObjectId(datum) != InvalidOid) {
|
||||
basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
|
||||
if (basetypelem != InvalidOid)
|
||||
{
|
||||
HeapTuple tup;
|
||||
|
||||
tup = SearchSysCache( TYPEOID
|
||||
, datum
|
||||
, 0, 0, 0
|
||||
);
|
||||
|
||||
elemName = NameStr(((Form_pg_type) GETSTRUCT(tup))->typname);
|
||||
|
||||
tup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(basetypelem),
|
||||
0, 0, 0);
|
||||
elemName = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(tup))->typname));
|
||||
ReleaseSysCache(tup);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Run through constraints manually avoids the additional
|
||||
* Run through constraints manually to avoid the additional
|
||||
* processing conducted by DefineRelation() and friends.
|
||||
*
|
||||
* Besides, we don't want any constraints to be cooked. We'll
|
||||
@@ -668,20 +637,13 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
*/
|
||||
foreach(listptr, schema)
|
||||
{
|
||||
Constraint *colDef = lfirst(listptr);
|
||||
bool nullDefined = false;
|
||||
Node *expr;
|
||||
Constraint *colDef = lfirst(listptr);
|
||||
|
||||
/* Used for the statement transformation */
|
||||
ParseState *pstate;
|
||||
|
||||
/*
|
||||
* Create a dummy ParseState and insert the target relation as its
|
||||
* sole rangetable entry. We need a ParseState for transformExpr.
|
||||
*/
|
||||
pstate = make_parsestate(NULL);
|
||||
|
||||
switch(colDef->contype) {
|
||||
switch (colDef->contype)
|
||||
{
|
||||
/*
|
||||
* The inherited default value may be overridden by the user
|
||||
* with the DEFAULT <expr> statement.
|
||||
@@ -690,27 +652,26 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
* don't want to cook or fiddle too much.
|
||||
*/
|
||||
case CONSTR_DEFAULT:
|
||||
|
||||
/* Create a dummy ParseState for transformExpr */
|
||||
pstate = make_parsestate(NULL);
|
||||
/*
|
||||
* Cook the colDef->raw_expr into an expression to ensure
|
||||
* that it can be done. We store the text version of the
|
||||
* raw value.
|
||||
*
|
||||
* Cook the colDef->raw_expr into an expression.
|
||||
* Note: Name is strictly for error message
|
||||
*/
|
||||
expr = cookDefault(pstate, colDef->raw_expr
|
||||
, typeTup->t_data->t_oid
|
||||
, stmt->typename->typmod
|
||||
, stmt->typename->name);
|
||||
|
||||
/* Binary default required */
|
||||
expr = cookDefault(pstate, colDef->raw_expr,
|
||||
typeTup->t_data->t_oid,
|
||||
stmt->typename->typmod,
|
||||
stmt->typename->name);
|
||||
/*
|
||||
* Expression must be stored as a nodeToString result,
|
||||
* but we also require a valid textual representation
|
||||
* (mainly to make life easier for pg_dump).
|
||||
*/
|
||||
defaultValue = deparse_expression(expr,
|
||||
deparse_context_for(stmt->domainname,
|
||||
InvalidOid),
|
||||
false);
|
||||
|
||||
defaultValueBin = nodeToString(expr);
|
||||
|
||||
break;
|
||||
|
||||
/*
|
||||
@@ -723,7 +684,6 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
typNotNull = true;
|
||||
nullDefined = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CONSTR_NULL:
|
||||
@@ -733,31 +693,31 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
typNotNull = false;
|
||||
nullDefined = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CONSTR_UNIQUE:
|
||||
elog(ERROR, "CREATE DOMAIN / UNIQUE indecies not supported");
|
||||
elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
|
||||
break;
|
||||
|
||||
case CONSTR_PRIMARY:
|
||||
elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indecies not supported");
|
||||
elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
|
||||
break;
|
||||
|
||||
|
||||
case CONSTR_CHECK:
|
||||
|
||||
elog(ERROR, "defineDomain: CHECK Constraints not supported");
|
||||
elog(ERROR, "DefineDomain: CHECK Constraints not supported");
|
||||
break;
|
||||
|
||||
case CONSTR_ATTR_DEFERRABLE:
|
||||
case CONSTR_ATTR_NOT_DEFERRABLE:
|
||||
case CONSTR_ATTR_DEFERRED:
|
||||
case CONSTR_ATTR_IMMEDIATE:
|
||||
elog(ERROR, "defineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
|
||||
elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "DefineDomain: unrecognized constraint node type");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -776,8 +736,8 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
sendName, /* send procedure */
|
||||
elemName, /* element type name */
|
||||
typeName, /* base type name */
|
||||
defaultValue, /* default type value */
|
||||
defaultValueBin, /* default type value */
|
||||
defaultValue, /* default type value (text) */
|
||||
defaultValueBin, /* default type value (binary) */
|
||||
byValue, /* passed by value */
|
||||
alignment, /* required alignment */
|
||||
storage, /* TOAST strategy */
|
||||
@@ -789,10 +749,8 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
* Now we can clean up.
|
||||
*/
|
||||
ReleaseSysCache(typeTup);
|
||||
heap_close(pg_type_rel, NoLock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DefineType
|
||||
* Registers a new type.
|
||||
@@ -808,8 +766,6 @@ DefineType(char *typeName, List *parameters)
|
||||
char *sendName = NULL;
|
||||
char *receiveName = NULL;
|
||||
char *defaultValue = NULL;
|
||||
char *defaultValueBin = NULL;
|
||||
Node *defaultRaw = (Node *) NULL;
|
||||
bool byValue = false;
|
||||
char delimiter = DEFAULT_TYPDELIM;
|
||||
char *shadow_type;
|
||||
@@ -851,7 +807,7 @@ DefineType(char *typeName, List *parameters)
|
||||
else if (strcasecmp(defel->defname, "element") == 0)
|
||||
elemName = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "default") == 0)
|
||||
defaultRaw = defel->arg;
|
||||
defaultValue = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
|
||||
byValue = true;
|
||||
else if (strcasecmp(defel->defname, "alignment") == 0)
|
||||
@@ -911,32 +867,6 @@ DefineType(char *typeName, List *parameters)
|
||||
if (outputName == NULL)
|
||||
elog(ERROR, "Define: \"output\" unspecified");
|
||||
|
||||
|
||||
if (defaultRaw) {
|
||||
Node *expr;
|
||||
ParseState *pstate;
|
||||
|
||||
/*
|
||||
* Create a dummy ParseState and insert the target relation as its
|
||||
* sole rangetable entry. We need a ParseState for transformExpr.
|
||||
*/
|
||||
pstate = make_parsestate(NULL);
|
||||
|
||||
expr = cookDefault(pstate, defaultRaw,
|
||||
InvalidOid,
|
||||
-1,
|
||||
typeName);
|
||||
|
||||
/* Binary default required */
|
||||
defaultValue = deparse_expression(expr,
|
||||
deparse_context_for(typeName,
|
||||
InvalidOid),
|
||||
false);
|
||||
|
||||
defaultValueBin = nodeToString(expr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* now have TypeCreate do all the real work.
|
||||
*/
|
||||
@@ -952,9 +882,9 @@ DefineType(char *typeName, List *parameters)
|
||||
receiveName, /* receive procedure */
|
||||
sendName, /* send procedure */
|
||||
elemName, /* element type name */
|
||||
NULL, /* base type name (Non-zero for domains) */
|
||||
NULL, /* base type name (only for domains) */
|
||||
defaultValue, /* default type value */
|
||||
defaultValueBin, /* default type value (Binary form) */
|
||||
NULL, /* no binary form available */
|
||||
byValue, /* passed by value */
|
||||
alignment, /* required alignment */
|
||||
storage, /* TOAST strategy */
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.63 2002/03/06 06:09:33 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.64 2002/03/20 19:43:47 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -314,8 +314,7 @@ FuncIndexArgs(IndexInfo *indexInfo,
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
if (argTypes[i] != true_typeids[i] &&
|
||||
!IS_BINARY_COMPATIBLE(argTypes[i], true_typeids[i]))
|
||||
if (!IsBinaryCompatible(argTypes[i], true_typeids[i]))
|
||||
func_error("DefineIndex", funcIndex->name, nargs, argTypes,
|
||||
"Index function must be binary-compatible with table datatype");
|
||||
}
|
||||
@@ -418,8 +417,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype;
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
if (attrType != opInputType &&
|
||||
!IS_BINARY_COMPATIBLE(attrType, opInputType))
|
||||
if (!IsBinaryCompatible(attrType, opInputType))
|
||||
elog(ERROR, "operator class \"%s\" does not accept data type %s",
|
||||
attribute->class, format_type_be(attrType));
|
||||
|
||||
@@ -470,7 +468,7 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId)
|
||||
nexact++;
|
||||
exactOid = tuple->t_data->t_oid;
|
||||
}
|
||||
else if (IS_BINARY_COMPATIBLE(opclass->opcintype, attrType))
|
||||
else if (IsBinaryCompatible(opclass->opcintype, attrType))
|
||||
{
|
||||
ncompatible++;
|
||||
compatibleOid = tuple->t_data->t_oid;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.69 2002/03/19 02:18:16 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.70 2002/03/20 19:43:49 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -282,23 +282,18 @@ RemoveType(char *typeName) /* type name to be removed */
|
||||
* use it.
|
||||
*/
|
||||
void
|
||||
RemoveDomain(char *domainName, int behavior) /* domain name to be removed */
|
||||
RemoveDomain(char *domainName, int behavior)
|
||||
{
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
TupleDesc description;
|
||||
char typtype;
|
||||
bool isnull;
|
||||
|
||||
|
||||
/* Domains are stored as types. Check for permissions on the type */
|
||||
if (!pg_ownercheck(GetUserId(), domainName, TYPENAME))
|
||||
elog(ERROR, "RemoveDomain: type '%s': permission denied",
|
||||
domainName);
|
||||
|
||||
|
||||
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||
description = RelationGetDescr(relation);
|
||||
|
||||
tup = SearchSysCache(TYPENAME,
|
||||
PointerGetDatum(domainName),
|
||||
@@ -306,14 +301,11 @@ RemoveDomain(char *domainName, int behavior) /* domain name to be removed */
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "RemoveType: type '%s' does not exist", domainName);
|
||||
|
||||
|
||||
/* Check that this is actually a domain */
|
||||
typtype = DatumGetChar(heap_getattr(tup, Anum_pg_type_typtype, description, &isnull));
|
||||
Assert(!isnull);
|
||||
typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
|
||||
|
||||
if (typtype != 'd') {
|
||||
if (typtype != 'd')
|
||||
elog(ERROR, "%s is not a domain", domainName);
|
||||
}
|
||||
|
||||
/* CASCADE unsupported */
|
||||
if (behavior == CASCADE) {
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.79 2002/03/02 21:39:25 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.80 2002/03/20 19:43:54 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -896,8 +896,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
|
||||
*/
|
||||
Oid inputType = exprType(aggref->target);
|
||||
|
||||
if (inputType != aggform->aggtranstype &&
|
||||
!IS_BINARY_COMPATIBLE(inputType, aggform->aggtranstype))
|
||||
if (!IsBinaryCompatible(inputType, aggform->aggtranstype))
|
||||
elog(ERROR, "Aggregate %s needs to have compatible input type and transition type",
|
||||
aggname);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.170 2002/03/19 02:18:16 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.171 2002/03/20 19:43:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2026,6 +2026,7 @@ _copyDropStmt(DropStmt *from)
|
||||
|
||||
Node_Copy(from, newnode, names);
|
||||
newnode->removeType = from->removeType;
|
||||
newnode->behavior = from->behavior;
|
||||
|
||||
return newnode;
|
||||
}
|
||||
@@ -2238,7 +2239,6 @@ _copyCreateDomainStmt(CreateDomainStmt *from)
|
||||
|
||||
if (from->domainname)
|
||||
newnode->domainname = pstrdup(from->domainname);
|
||||
|
||||
Node_Copy(from, newnode, typename);
|
||||
Node_Copy(from, newnode, constraints);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.118 2002/03/19 02:18:16 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.119 2002/03/20 19:44:01 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -878,6 +878,8 @@ _equalDropStmt(DropStmt *a, DropStmt *b)
|
||||
return false;
|
||||
if (a->removeType != b->removeType)
|
||||
return false;
|
||||
if (a->behavior != b->behavior)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,16 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.26 2001/03/22 03:59:32 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Creator functions in POSTGRES 4.2 are generated automatically. Most of
|
||||
* them are rarely used. Now we don't generate them any more. If you want
|
||||
* one, you have to write it yourself.
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
* Andrew Yu Oct 20, 1994 file creation
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.27 2002/03/20 19:44:04 tgl Exp $
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
@@ -184,3 +175,19 @@ makeAttr(char *relname, char *attname)
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
* makeRelabelType -
|
||||
* creates a RelabelType node
|
||||
*/
|
||||
RelabelType *
|
||||
makeRelabelType(Node *arg, Oid rtype, int32 rtypmod)
|
||||
{
|
||||
RelabelType *r = makeNode(RelabelType);
|
||||
|
||||
r->arg = arg;
|
||||
r->resulttype = rtype;
|
||||
r->resulttypmod = rtypmod;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.113 2002/03/02 21:39:26 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.114 2002/03/20 19:44:09 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -900,9 +900,9 @@ indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left)
|
||||
*/
|
||||
if (ltype == indexkeytype && rtype == indexkeytype)
|
||||
return InvalidOid; /* no chance for a different operator */
|
||||
if (ltype != indexkeytype && !IS_BINARY_COMPATIBLE(ltype, indexkeytype))
|
||||
if (!IsBinaryCompatible(ltype, indexkeytype))
|
||||
return InvalidOid;
|
||||
if (rtype != indexkeytype && !IS_BINARY_COMPATIBLE(rtype, indexkeytype))
|
||||
if (!IsBinaryCompatible(rtype, indexkeytype))
|
||||
return InvalidOid;
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.49 2002/03/19 02:18:17 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.50 2002/03/20 19:44:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -355,9 +355,9 @@ build_column_default(Relation rel, int attrno)
|
||||
Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
|
||||
Oid atttype = att_tup->atttypid;
|
||||
int32 atttypmod = att_tup->atttypmod;
|
||||
int16 typlen;
|
||||
bool typbyval;
|
||||
Node *expr;
|
||||
int16 typlen = att_tup->attlen;
|
||||
bool typbyval = att_tup->attbyval;
|
||||
Node *expr = NULL;
|
||||
|
||||
/*
|
||||
* Scan to see if relation has a default for this column.
|
||||
@@ -371,110 +371,86 @@ build_column_default(Relation rel, int attrno)
|
||||
{
|
||||
if (attrno == defval[ndef].adnum)
|
||||
{
|
||||
Oid type_id;
|
||||
|
||||
/*
|
||||
* Found it, convert string representation to node tree.
|
||||
*/
|
||||
expr = stringToNode(defval[ndef].adbin);
|
||||
|
||||
/*
|
||||
* Make sure the value is coerced to the target column
|
||||
* type (might not be right type yet if it's not a
|
||||
* constant!) This should match the parser's processing of
|
||||
* non-defaulted expressions --- see
|
||||
* updateTargetListEntry().
|
||||
*/
|
||||
type_id = exprType(expr);
|
||||
|
||||
if (type_id != atttype)
|
||||
{
|
||||
expr = CoerceTargetExpr(NULL, expr, type_id,
|
||||
getBaseType(atttype), atttypmod);
|
||||
|
||||
/*
|
||||
* This really shouldn't fail; should have checked the
|
||||
* default's type when it was created ...
|
||||
*/
|
||||
if (expr == NULL)
|
||||
elog(ERROR, "Column \"%s\" is of type %s"
|
||||
" but default expression is of type %s"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
NameStr(att_tup->attname),
|
||||
format_type_be(atttype),
|
||||
format_type_be(type_id));
|
||||
}
|
||||
|
||||
/*
|
||||
* If the column is a fixed-length type, it may need a
|
||||
* length coercion as well as a type coercion.
|
||||
*/
|
||||
expr = coerce_type_typmod(NULL, expr,
|
||||
atttype, atttypmod);
|
||||
return expr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No per-column default, so look for a default for the type itself.
|
||||
* If there isn't one, we generate a NULL constant of the correct
|
||||
* type.
|
||||
*/
|
||||
if (att_tup->attisset)
|
||||
if (expr == NULL)
|
||||
{
|
||||
/*
|
||||
* Set attributes are represented as OIDs no matter what the set
|
||||
* element type is, and the element type's default is irrelevant
|
||||
* too.
|
||||
* No per-column default, so look for a default for the type itself.
|
||||
*/
|
||||
typlen = sizeof(Oid);
|
||||
typbyval = true;
|
||||
if (att_tup->attisset)
|
||||
{
|
||||
/*
|
||||
* Set attributes are represented as OIDs no matter what the set
|
||||
* element type is, and the element type's default is irrelevant
|
||||
* too.
|
||||
*/
|
||||
typlen = sizeof(Oid);
|
||||
typbyval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
expr = get_typdefault(atttype);
|
||||
}
|
||||
}
|
||||
|
||||
if (expr == NULL)
|
||||
{
|
||||
/*
|
||||
* No default anywhere, so generate a NULL constant.
|
||||
*/
|
||||
expr = (Node *) makeConst(atttype,
|
||||
typlen,
|
||||
(Datum) 0,
|
||||
true,
|
||||
true, /* isnull */
|
||||
typbyval,
|
||||
false, /* not a set */
|
||||
false, /* not a set */
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _DROP_COLUMN_HACK__
|
||||
if (COLUMN_IS_DROPPED(att_tup))
|
||||
Oid exprtype;
|
||||
|
||||
/*
|
||||
* Make sure the value is coerced to the target column
|
||||
* type (might not be right type yet if it's not a
|
||||
* constant!) This should match the parser's processing of
|
||||
* non-defaulted expressions --- see
|
||||
* updateTargetListEntry().
|
||||
*/
|
||||
exprtype = exprType(expr);
|
||||
|
||||
if (exprtype != atttype)
|
||||
{
|
||||
expr = CoerceTargetExpr(NULL, expr, exprtype,
|
||||
atttype, atttypmod);
|
||||
|
||||
expr = (Node *) makeConst(atttype,
|
||||
typlen,
|
||||
(Datum) 0,
|
||||
true,
|
||||
typbyval,
|
||||
false, /* not a set */
|
||||
false);
|
||||
/*
|
||||
* This really shouldn't fail; should have checked the
|
||||
* default's type when it was created ...
|
||||
*/
|
||||
if (expr == NULL)
|
||||
elog(ERROR, "Column \"%s\" is of type %s"
|
||||
" but default expression is of type %s"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
NameStr(att_tup->attname),
|
||||
format_type_be(atttype),
|
||||
format_type_be(exprtype));
|
||||
}
|
||||
else
|
||||
#endif /* _DROP_COLUMN_HACK__ */
|
||||
expr = get_typdefault(atttype, atttypmod);
|
||||
|
||||
if (expr == NULL) {
|
||||
expr = (Node *) makeConst(atttype,
|
||||
typlen,
|
||||
(Datum) 0,
|
||||
true,
|
||||
typbyval,
|
||||
false, /* not a set */
|
||||
false);
|
||||
}
|
||||
get_typlenbyval(atttype, &typlen, &typbyval);
|
||||
/*
|
||||
* If the column is a fixed-length type, it may need a
|
||||
* length coercion as well as a type coercion.
|
||||
*/
|
||||
expr = coerce_type_typmod(NULL, expr, atttype, atttypmod);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the column is a fixed-length type, it may need a length coercion
|
||||
* as well as a type coercion, as well as direction to the final type.
|
||||
*/
|
||||
expr = coerce_type_typmod(NULL, expr,
|
||||
atttype, atttypmod);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.293 2002/03/19 12:52:20 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.294 2002/03/20 19:44:21 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -135,8 +135,7 @@ static void doNegateFloat(Value *v);
|
||||
ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
|
||||
CopyStmt, CreateAsStmt, CreateDomainStmt, CreateGroupStmt, CreatePLangStmt,
|
||||
CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
|
||||
CreateUserStmt, CreatedbStmt, CursorStmt,
|
||||
DefineStmt, DeleteStmt,
|
||||
CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
|
||||
DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
|
||||
DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
|
||||
GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
|
||||
@@ -151,7 +150,7 @@ static void doNegateFloat(Value *v);
|
||||
simple_select
|
||||
|
||||
%type <node> alter_column_default
|
||||
%type <ival> drop_behavior
|
||||
%type <ival> drop_behavior, opt_drop_behavior
|
||||
|
||||
%type <list> createdb_opt_list, createdb_opt_item
|
||||
%type <boolean> opt_equal
|
||||
@@ -1181,6 +1180,10 @@ drop_behavior: CASCADE { $$ = CASCADE; }
|
||||
| RESTRICT { $$ = RESTRICT; }
|
||||
;
|
||||
|
||||
opt_drop_behavior: CASCADE { $$ = CASCADE; }
|
||||
| RESTRICT { $$ = RESTRICT; }
|
||||
| /* EMPTY */ { $$ = RESTRICT; /* default */ }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -2030,22 +2033,13 @@ def_list: def_elem { $$ = makeList1($1); }
|
||||
| def_list ',' def_elem { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
def_elem: DEFAULT '=' b_expr
|
||||
{
|
||||
$$ = makeNode(DefElem);
|
||||
$$->defname = "default";
|
||||
if (exprIsNullConstant($3))
|
||||
$$->arg = (Node *)NULL;
|
||||
else
|
||||
$$->arg = $3;
|
||||
}
|
||||
| ColId '=' def_arg
|
||||
def_elem: ColLabel '=' def_arg
|
||||
{
|
||||
$$ = makeNode(DefElem);
|
||||
$$->defname = $1;
|
||||
$$->arg = (Node *)$3;
|
||||
}
|
||||
| ColId
|
||||
| ColLabel
|
||||
{
|
||||
$$ = makeNode(DefElem);
|
||||
$$->defname = $1;
|
||||
@@ -2069,19 +2063,11 @@ def_arg: func_return { $$ = (Node *)$1; }
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
DropStmt: DROP drop_type name_list
|
||||
DropStmt: DROP drop_type name_list opt_drop_behavior
|
||||
{
|
||||
DropStmt *n = makeNode(DropStmt);
|
||||
n->removeType = $2;
|
||||
n->names = $3;
|
||||
n->behavior = RESTRICT; /* Restricted by default */
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| DROP DOMAIN_P name_list drop_behavior
|
||||
{
|
||||
DropStmt *n = makeNode(DropStmt);
|
||||
n->removeType = DROP_DOMAIN_P;
|
||||
n->names = $3;
|
||||
n->behavior = $4;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
@@ -2092,7 +2078,8 @@ drop_type: TABLE { $$ = DROP_TABLE; }
|
||||
| VIEW { $$ = DROP_VIEW; }
|
||||
| INDEX { $$ = DROP_INDEX; }
|
||||
| RULE { $$ = DROP_RULE; }
|
||||
| TYPE_P { $$ = DROP_TYPE_P; }
|
||||
| TYPE_P { $$ = DROP_TYPE; }
|
||||
| DOMAIN_P { $$ = DROP_DOMAIN; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -3194,22 +3181,6 @@ createdb_opt_item: LOCATION opt_equal Sconst
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* DROP DATABASE
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
DropdbStmt: DROP DATABASE database_name
|
||||
{
|
||||
DropdbStmt *n = makeNode(DropdbStmt);
|
||||
n->dbname = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
* Though the equals sign doesn't match other WITH options, pg_dump uses
|
||||
* equals for backward compability, and it doesn't seem worth remove it.
|
||||
@@ -3221,8 +3192,22 @@ opt_equal: '=' { $$ = TRUE; }
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* ALTER DATABASE
|
||||
* DROP DATABASE
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
DropdbStmt: DROP DATABASE database_name
|
||||
{
|
||||
DropdbStmt *n = makeNode(DropdbStmt);
|
||||
n->dbname = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* ALTER DATABASE
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
@@ -8,25 +8,31 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.67 2002/03/19 02:18:20 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.68 2002/03/20 19:44:22 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
Oid DemoteType(Oid inType);
|
||||
Oid PromoteTypeToNext(Oid inType);
|
||||
|
||||
static Oid PreferredType(CATEGORY category, Oid type);
|
||||
static Node *build_func_call(Oid funcid, Oid rettype, List *args);
|
||||
static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
|
||||
Oid secondArgType);
|
||||
|
||||
|
||||
/* coerce_type()
|
||||
@@ -87,32 +93,28 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
|
||||
result = (Node *) newcon;
|
||||
}
|
||||
else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
|
||||
else if (IsBinaryCompatible(inputTypeId, targetTypeId))
|
||||
{
|
||||
/*
|
||||
* We don't really need to do a conversion, but we do need to
|
||||
* attach a RelabelType node so that the expression will be seen
|
||||
* to have the intended type when inspected by higher-level code.
|
||||
*/
|
||||
RelabelType *relabel = makeNode(RelabelType);
|
||||
|
||||
relabel->arg = node;
|
||||
relabel->resulttype = targetTypeId;
|
||||
|
||||
/*
|
||||
*
|
||||
* XXX could we label result with exprTypmod(node) instead of
|
||||
* default -1 typmod, to save a possible length-coercion later?
|
||||
* Would work if both types have same interpretation of typmod,
|
||||
* which is likely but not certain.
|
||||
*/
|
||||
relabel->resulttypmod = -1;
|
||||
|
||||
result = (Node *) relabel;
|
||||
result = (Node *) makeRelabelType(node, targetTypeId, -1);
|
||||
}
|
||||
else if (typeInheritsFrom(inputTypeId, targetTypeId))
|
||||
{
|
||||
/* Input class type is a subclass of target, so nothing to do */
|
||||
result = node;
|
||||
/*
|
||||
* Input class type is a subclass of target, so nothing to do
|
||||
* --- except relabel the type. This is binary compatibility
|
||||
* for complex types.
|
||||
*/
|
||||
result = (Node *) makeRelabelType(node, targetTypeId, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -121,21 +123,24 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
* (caller should have determined that there is one), and generate
|
||||
* an expression tree representing run-time application of the
|
||||
* conversion function.
|
||||
*
|
||||
* For domains, we use the coercion function for the base type.
|
||||
*/
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
Oid baseTypeId = getBaseType(targetTypeId);
|
||||
Oid funcId;
|
||||
|
||||
n->funcname = typeidTypeName(targetTypeId);
|
||||
n->args = makeList1(node);
|
||||
n->agg_star = false;
|
||||
n->agg_distinct = false;
|
||||
funcId = find_coercion_function(baseTypeId,
|
||||
getBaseType(inputTypeId),
|
||||
InvalidOid);
|
||||
if (!OidIsValid(funcId))
|
||||
elog(ERROR, "coerce_type: no conversion function from %s to %s",
|
||||
format_type_be(inputTypeId), format_type_be(targetTypeId));
|
||||
|
||||
result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST);
|
||||
result = build_func_call(funcId, baseTypeId, makeList1(node));
|
||||
|
||||
/* safety check that we got the right thing */
|
||||
if (exprType(result) != targetTypeId)
|
||||
elog(ERROR, "coerce_type: conversion function %s produced %s",
|
||||
typeidTypeName(targetTypeId),
|
||||
typeidTypeName(exprType(result)));
|
||||
/* if domain, relabel with domain type ID */
|
||||
if (targetTypeId != baseTypeId)
|
||||
result = (Node *) makeRelabelType(result, targetTypeId, -1);
|
||||
|
||||
/*
|
||||
* If the input is a constant, apply the type conversion function
|
||||
@@ -152,7 +157,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
* nodes that mustn't be collapsed. (It'd be a lot cleaner to
|
||||
* make a separate node type for that purpose...)
|
||||
*/
|
||||
if (IsA(node, Const) &&!((Const *) node)->constisnull)
|
||||
if (IsA(node, Const) &&
|
||||
!((Const *) node)->constisnull)
|
||||
result = eval_const_expressions(result);
|
||||
}
|
||||
|
||||
@@ -169,23 +175,18 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
*
|
||||
* Notes:
|
||||
* This uses the same mechanism as the CAST() SQL construct in gram.y.
|
||||
* We should also check the function return type on candidate conversion
|
||||
* routines just to be safe but we do not do that yet...
|
||||
* - thomas 1998-03-31
|
||||
*/
|
||||
bool
|
||||
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||
{
|
||||
int i;
|
||||
HeapTuple ftup;
|
||||
Form_pg_proc pform;
|
||||
Oid oid_array[FUNC_MAX_ARGS];
|
||||
|
||||
/* run through argument list... */
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
Oid inputTypeId = input_typeids[i];
|
||||
Oid targetTypeId = func_typeids[i];
|
||||
Oid funcId;
|
||||
|
||||
/* no problem if same type */
|
||||
if (inputTypeId == targetTypeId)
|
||||
@@ -195,7 +196,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||
* one of the known-good transparent conversions? then drop
|
||||
* through...
|
||||
*/
|
||||
if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
|
||||
if (IsBinaryCompatible(inputTypeId, targetTypeId))
|
||||
continue;
|
||||
|
||||
/* don't know what to do for the output type? then quit... */
|
||||
@@ -232,25 +233,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||
* Else, try for explicit conversion using functions: look for a
|
||||
* single-argument function named with the target type name and
|
||||
* accepting the source type.
|
||||
*
|
||||
* If either type is a domain, use its base type instead.
|
||||
*/
|
||||
MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
oid_array[0] = inputTypeId;
|
||||
|
||||
ftup = SearchSysCache(PROCNAME,
|
||||
PointerGetDatum(typeidTypeName(targetTypeId)),
|
||||
Int32GetDatum(1),
|
||||
PointerGetDatum(oid_array),
|
||||
0);
|
||||
if (!HeapTupleIsValid(ftup))
|
||||
funcId = find_coercion_function(getBaseType(targetTypeId),
|
||||
getBaseType(inputTypeId),
|
||||
InvalidOid);
|
||||
if (!OidIsValid(funcId))
|
||||
return false;
|
||||
/* Make sure the function's result type is as expected, too */
|
||||
pform = (Form_pg_proc) GETSTRUCT(ftup);
|
||||
if (pform->prorettype != targetTypeId)
|
||||
{
|
||||
ReleaseSysCache(ftup);
|
||||
return false;
|
||||
}
|
||||
ReleaseSysCache(ftup);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -277,8 +267,8 @@ Node *
|
||||
coerce_type_typmod(ParseState *pstate, Node *node,
|
||||
Oid targetTypeId, int32 atttypmod)
|
||||
{
|
||||
char *funcname;
|
||||
Oid oid_array[FUNC_MAX_ARGS];
|
||||
Oid baseTypeId;
|
||||
Oid funcId;
|
||||
|
||||
/*
|
||||
* A negative typmod is assumed to mean that no coercion is wanted.
|
||||
@@ -286,30 +276,28 @@ coerce_type_typmod(ParseState *pstate, Node *node,
|
||||
if (atttypmod < 0 || atttypmod == exprTypmod(node))
|
||||
return node;
|
||||
|
||||
funcname = typeidTypeName(targetTypeId);
|
||||
MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
oid_array[0] = targetTypeId;
|
||||
oid_array[1] = INT4OID;
|
||||
/* If given type is a domain, use base type instead */
|
||||
baseTypeId = getBaseType(targetTypeId);
|
||||
|
||||
/* attempt to find with arguments exactly as specified... */
|
||||
if (SearchSysCacheExists(PROCNAME,
|
||||
PointerGetDatum(funcname),
|
||||
Int32GetDatum(2),
|
||||
PointerGetDatum(oid_array),
|
||||
0))
|
||||
funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID);
|
||||
|
||||
if (OidIsValid(funcId))
|
||||
{
|
||||
A_Const *cons = makeNode(A_Const);
|
||||
FuncCall *func = makeNode(FuncCall);
|
||||
Const *cons;
|
||||
|
||||
cons->val.type = T_Integer;
|
||||
cons->val.val.ival = atttypmod;
|
||||
cons = makeConst(INT4OID,
|
||||
sizeof(int32),
|
||||
Int32GetDatum(atttypmod),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false);
|
||||
|
||||
func->funcname = funcname;
|
||||
func->args = makeList2(node, cons);
|
||||
func->agg_star = false;
|
||||
func->agg_distinct = false;
|
||||
node = build_func_call(funcId, baseTypeId, makeList2(node, cons));
|
||||
|
||||
node = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
|
||||
/* relabel if it's domain case */
|
||||
if (targetTypeId != baseTypeId)
|
||||
node = (Node *) makeRelabelType(node, targetTypeId, atttypmod);
|
||||
}
|
||||
|
||||
return node;
|
||||
@@ -532,6 +520,64 @@ TypeCategory(Oid inType)
|
||||
} /* TypeCategory() */
|
||||
|
||||
|
||||
/* IsBinaryCompatible()
|
||||
* Check if two types are binary-compatible.
|
||||
*
|
||||
* This notion allows us to cheat and directly exchange values without
|
||||
* going through the trouble of calling a conversion function.
|
||||
*
|
||||
* XXX This should be moved to system catalog lookups
|
||||
* to allow for better type extensibility.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This macro describes hard-coded knowledge of binary compatibility
|
||||
* for built-in types.
|
||||
*/
|
||||
#define IS_BINARY_COMPATIBLE(a,b) \
|
||||
(((a) == BPCHAROID && (b) == TEXTOID) \
|
||||
|| ((a) == BPCHAROID && (b) == VARCHAROID) \
|
||||
|| ((a) == VARCHAROID && (b) == TEXTOID) \
|
||||
|| ((a) == VARCHAROID && (b) == BPCHAROID) \
|
||||
|| ((a) == TEXTOID && (b) == BPCHAROID) \
|
||||
|| ((a) == TEXTOID && (b) == VARCHAROID) \
|
||||
|| ((a) == OIDOID && (b) == INT4OID) \
|
||||
|| ((a) == OIDOID && (b) == REGPROCOID) \
|
||||
|| ((a) == INT4OID && (b) == OIDOID) \
|
||||
|| ((a) == INT4OID && (b) == REGPROCOID) \
|
||||
|| ((a) == REGPROCOID && (b) == OIDOID) \
|
||||
|| ((a) == REGPROCOID && (b) == INT4OID) \
|
||||
|| ((a) == ABSTIMEOID && (b) == INT4OID) \
|
||||
|| ((a) == INT4OID && (b) == ABSTIMEOID) \
|
||||
|| ((a) == RELTIMEOID && (b) == INT4OID) \
|
||||
|| ((a) == INT4OID && (b) == RELTIMEOID) \
|
||||
|| ((a) == INETOID && (b) == CIDROID) \
|
||||
|| ((a) == CIDROID && (b) == INETOID) \
|
||||
|| ((a) == BITOID && (b) == VARBITOID) \
|
||||
|| ((a) == VARBITOID && (b) == BITOID))
|
||||
|
||||
bool
|
||||
IsBinaryCompatible(Oid type1, Oid type2)
|
||||
{
|
||||
if (type1 == type2)
|
||||
return true;
|
||||
if (IS_BINARY_COMPATIBLE(type1, type2))
|
||||
return true;
|
||||
/*
|
||||
* Perhaps the types are domains; if so, look at their base types
|
||||
*/
|
||||
if (OidIsValid(type1))
|
||||
type1 = getBaseType(type1);
|
||||
if (OidIsValid(type2))
|
||||
type2 = getBaseType(type2);
|
||||
if (type1 == type2)
|
||||
return true;
|
||||
if (IS_BINARY_COMPATIBLE(type1, type2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* IsPreferredType()
|
||||
* Check if this type is a preferred type.
|
||||
* XXX This should be moved to system catalog lookups
|
||||
@@ -606,31 +652,81 @@ PreferredType(CATEGORY category, Oid type)
|
||||
return result;
|
||||
} /* PreferredType() */
|
||||
|
||||
/*
|
||||
* find_coercion_function
|
||||
* Look for a coercion function between two types.
|
||||
*
|
||||
* A coercion function must be named after (the internal name of) its
|
||||
* result type, and must accept exactly the specified input type.
|
||||
*
|
||||
* This routine is also used to look for length-coercion functions, which
|
||||
* are similar but accept a second argument. secondArgType is the type
|
||||
* of the second argument (normally INT4OID), or InvalidOid if we are
|
||||
* looking for a regular coercion function.
|
||||
*
|
||||
* If a function is found, return its pg_proc OID; else return InvalidOid.
|
||||
*/
|
||||
static Oid
|
||||
find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType)
|
||||
{
|
||||
char *funcname;
|
||||
Oid oid_array[FUNC_MAX_ARGS];
|
||||
int nargs;
|
||||
HeapTuple ftup;
|
||||
Form_pg_proc pform;
|
||||
Oid funcid;
|
||||
|
||||
funcname = typeidTypeName(targetTypeId);
|
||||
MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
oid_array[0] = inputTypeId;
|
||||
if (OidIsValid(secondArgType))
|
||||
{
|
||||
oid_array[1] = secondArgType;
|
||||
nargs = 2;
|
||||
}
|
||||
else
|
||||
nargs = 1;
|
||||
|
||||
ftup = SearchSysCache(PROCNAME,
|
||||
PointerGetDatum(funcname),
|
||||
Int32GetDatum(nargs),
|
||||
PointerGetDatum(oid_array),
|
||||
0);
|
||||
if (!HeapTupleIsValid(ftup))
|
||||
return InvalidOid;
|
||||
/* Make sure the function's result type is as expected, too */
|
||||
pform = (Form_pg_proc) GETSTRUCT(ftup);
|
||||
if (pform->prorettype != targetTypeId)
|
||||
{
|
||||
ReleaseSysCache(ftup);
|
||||
return InvalidOid;
|
||||
}
|
||||
funcid = ftup->t_data->t_oid;
|
||||
ReleaseSysCache(ftup);
|
||||
return funcid;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the targetTypeId is a domain, we really want to coerce
|
||||
* the tuple to the domain type -- not the domain itself
|
||||
* Build an expression tree representing a function call.
|
||||
*
|
||||
* The argument expressions must have been transformed already.
|
||||
*/
|
||||
Oid
|
||||
getBaseType(Oid inType)
|
||||
static Node *
|
||||
build_func_call(Oid funcid, Oid rettype, List *args)
|
||||
{
|
||||
HeapTuple tup;
|
||||
Form_pg_type typTup;
|
||||
Func *funcnode;
|
||||
Expr *expr;
|
||||
|
||||
tup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(inType),
|
||||
0, 0, 0);
|
||||
funcnode = makeNode(Func);
|
||||
funcnode->funcid = funcid;
|
||||
funcnode->functype = rettype;
|
||||
funcnode->func_fcache = NULL;
|
||||
|
||||
typTup = ((Form_pg_type) GETSTRUCT(tup));
|
||||
expr = makeNode(Expr);
|
||||
expr->typeOid = rettype;
|
||||
expr->opType = FUNC_EXPR;
|
||||
expr->oper = (Node *) funcnode;
|
||||
expr->args = args;
|
||||
|
||||
/*
|
||||
* Assume that typbasetype exists and is a base type, where inType
|
||||
* was a domain
|
||||
*/
|
||||
if (typTup->typtype == 'd')
|
||||
inType = typTup->typbasetype;
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
return inType;
|
||||
return (Node *) expr;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.109 2002/03/19 02:18:20 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.110 2002/03/20 19:44:25 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1027,8 +1027,7 @@ parser_typecast_expression(ParseState *pstate,
|
||||
if (inputType != targetType)
|
||||
{
|
||||
expr = CoerceTargetExpr(pstate, expr, inputType,
|
||||
getBaseType(targetType),
|
||||
typename->typmod);
|
||||
targetType, typename->typmod);
|
||||
if (expr == NULL)
|
||||
elog(ERROR, "Cannot cast type '%s' to '%s'",
|
||||
format_type_be(inputType),
|
||||
@@ -1040,7 +1039,7 @@ parser_typecast_expression(ParseState *pstate,
|
||||
* as well as a type coercion.
|
||||
*/
|
||||
expr = coerce_type_typmod(pstate, expr,
|
||||
getBaseType(targetType), typename->typmod);
|
||||
targetType, typename->typmod);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.117 2002/03/12 00:51:55 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.118 2002/03/20 19:44:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -575,8 +575,7 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
|
||||
{
|
||||
current_typeid = current_candidate->args[0];
|
||||
|
||||
if (current_typeid == typeid
|
||||
|| IS_BINARY_COMPATIBLE(current_typeid, typeid))
|
||||
if (IsBinaryCompatible(current_typeid, typeid))
|
||||
{
|
||||
last_candidate = current_candidate;
|
||||
ncandidates++;
|
||||
@@ -815,9 +814,7 @@ func_select_candidate(int nargs,
|
||||
{
|
||||
if (input_typeids[i] != UNKNOWNOID)
|
||||
{
|
||||
if (current_typeids[i] == input_typeids[i] ||
|
||||
IS_BINARY_COMPATIBLE(current_typeids[i],
|
||||
input_typeids[i]))
|
||||
if (IsBinaryCompatible(current_typeids[i], input_typeids[i]))
|
||||
nmatch++;
|
||||
}
|
||||
}
|
||||
@@ -1115,8 +1112,7 @@ func_get_detail(char *funcname,
|
||||
Node *arg1 = lfirst(fargs);
|
||||
|
||||
if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
|
||||
sourceType == targetType ||
|
||||
IS_BINARY_COMPATIBLE(sourceType, targetType))
|
||||
IsBinaryCompatible(sourceType, targetType))
|
||||
{
|
||||
/* Yup, it's a type coercion */
|
||||
*funcid = InvalidOid;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.52 2002/02/19 20:11:15 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.53 2002/03/20 19:44:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -354,9 +354,7 @@ oper_select_candidate(int nargs,
|
||||
{
|
||||
if (input_typeids[i] != UNKNOWNOID)
|
||||
{
|
||||
if (current_typeids[i] == input_typeids[i] ||
|
||||
IS_BINARY_COMPATIBLE(current_typeids[i],
|
||||
input_typeids[i]))
|
||||
if (IsBinaryCompatible(current_typeids[i], input_typeids[i]))
|
||||
nmatch++;
|
||||
}
|
||||
}
|
||||
@@ -736,10 +734,8 @@ compatible_oper(char *op, Oid arg1, Oid arg2, bool noError)
|
||||
|
||||
/* but is it good enough? */
|
||||
opform = (Form_pg_operator) GETSTRUCT(optup);
|
||||
if ((opform->oprleft == arg1 ||
|
||||
IS_BINARY_COMPATIBLE(opform->oprleft, arg1)) &&
|
||||
(opform->oprright == arg2 ||
|
||||
IS_BINARY_COMPATIBLE(opform->oprright, arg2)))
|
||||
if (IsBinaryCompatible(opform->oprleft, arg1) &&
|
||||
IsBinaryCompatible(opform->oprright, arg2))
|
||||
return optup;
|
||||
|
||||
/* nope... */
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.134 2002/03/19 02:58:19 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.135 2002/03/20 19:44:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -276,12 +276,12 @@ ProcessUtility(Node *parsetree,
|
||||
}
|
||||
break;
|
||||
|
||||
case DROP_TYPE_P:
|
||||
case DROP_TYPE:
|
||||
/* RemoveType does its own permissions checks */
|
||||
RemoveType(relname);
|
||||
break;
|
||||
|
||||
case DROP_DOMAIN_P:
|
||||
case DROP_DOMAIN:
|
||||
/* RemoveDomain does its own permissions checks */
|
||||
RemoveDomain(relname, stmt->behavior);
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.27 2002/03/19 02:18:21 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.28 2002/03/20 19:44:40 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -123,10 +123,10 @@ format_type_internal(Oid type_oid, int32 typemod,
|
||||
HeapTuple tuple;
|
||||
Oid array_base_type;
|
||||
int16 typlen;
|
||||
char typtype;
|
||||
bool is_array;
|
||||
char *name;
|
||||
char *buf;
|
||||
char typtype;
|
||||
|
||||
if (type_oid == InvalidOid && allow_invalid)
|
||||
return pstrdup("-");
|
||||
@@ -143,34 +143,16 @@ format_type_internal(Oid type_oid, int32 typemod,
|
||||
type_oid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if it's an array (and not a domain --- we don't want to show
|
||||
* the substructure of a domain type). Fixed-length array types such
|
||||
* as "name" shouldn't get deconstructed either.
|
||||
*/
|
||||
array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem;
|
||||
typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
|
||||
typtype = ((Form_pg_type) GETSTRUCT(tuple))->typtype;
|
||||
|
||||
/*
|
||||
* Domains look alot like arrays, so lets process them first, and return
|
||||
* back to avoid the array and 'standard' formatting procedures that are
|
||||
* use for base types.
|
||||
*/
|
||||
if (typtype == 'd') {
|
||||
name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
|
||||
|
||||
/*
|
||||
* Double-quote the name if it's not a standard identifier.
|
||||
* Note this is *necessary* for ruleutils.c's use.
|
||||
*/
|
||||
if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
|
||||
|| isdigit((unsigned char) name[0]))
|
||||
buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
|
||||
else
|
||||
buf = pstrdup(name);
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (array_base_type != InvalidOid && typlen < 0)
|
||||
if (array_base_type != InvalidOid && typlen < 0 && typtype != 'd')
|
||||
{
|
||||
/* Switch our attention to the array element type */
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
122
src/backend/utils/cache/lsyscache.c
vendored
122
src/backend/utils/cache/lsyscache.c
vendored
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.63 2002/03/19 02:18:21 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.64 2002/03/20 19:44:42 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "catalog/pg_shadow.h"
|
||||
#include "catalog/pg_statistic.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
@@ -818,19 +818,19 @@ get_typstorage(Oid typid)
|
||||
|
||||
/*
|
||||
* get_typdefault
|
||||
*
|
||||
* Given a type OID, return the type's default value, if any.
|
||||
* Returns FALSE if there is no default (effectively, default is NULL).
|
||||
* The result points to palloc'd storage for pass-by-reference types.
|
||||
*
|
||||
* The result is a palloc'd expression node tree, or NULL if there
|
||||
* is no defined default for the datatype.
|
||||
*
|
||||
* NB: caller should be prepared to coerce result to correct datatype;
|
||||
* the returned expression tree might produce something of the wrong type.
|
||||
*/
|
||||
Node *
|
||||
get_typdefault(Oid typid, int32 atttypmod)
|
||||
get_typdefault(Oid typid)
|
||||
{
|
||||
HeapTuple typeTuple;
|
||||
Form_pg_type type;
|
||||
Oid typinput;
|
||||
Oid typbasetype;
|
||||
char typtype;
|
||||
Datum datum;
|
||||
bool isNull;
|
||||
Node *expr;
|
||||
@@ -838,50 +838,104 @@ get_typdefault(Oid typid, int32 atttypmod)
|
||||
typeTuple = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(typid),
|
||||
0, 0, 0);
|
||||
|
||||
if (!HeapTupleIsValid(typeTuple))
|
||||
elog(ERROR, "get_typdefault: failed to lookup type %u", typid);
|
||||
|
||||
type = (Form_pg_type) GETSTRUCT(typeTuple);
|
||||
|
||||
typinput = type->typinput;
|
||||
typbasetype = type->typbasetype;
|
||||
typtype = type->typtype;
|
||||
|
||||
/*
|
||||
* typdefaultbin is potentially null, so don't try to access it as a
|
||||
* struct field. Must do it the hard way with SysCacheGetAttr.
|
||||
* typdefault and typdefaultbin are potentially null, so don't try to
|
||||
* access 'em as struct fields. Must do it the hard way with
|
||||
* SysCacheGetAttr.
|
||||
*/
|
||||
datum = SysCacheGetAttr(TYPEOID,
|
||||
typeTuple,
|
||||
Anum_pg_type_typdefaultbin,
|
||||
&isNull);
|
||||
|
||||
ReleaseSysCache(typeTuple);
|
||||
if (isNull)
|
||||
return (Node *) NULL;
|
||||
if (!isNull)
|
||||
{
|
||||
/* We have an expression default */
|
||||
expr = stringToNode(DatumGetCString(DirectFunctionCall1(textout,
|
||||
datum)));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Perhaps we have a plain literal default */
|
||||
datum = SysCacheGetAttr(TYPEOID,
|
||||
typeTuple,
|
||||
Anum_pg_type_typdefault,
|
||||
&isNull);
|
||||
|
||||
/* Convert Datum to a Node */
|
||||
expr = stringToNode(DatumGetCString(
|
||||
DirectFunctionCall1(textout, datum)));
|
||||
if (!isNull)
|
||||
{
|
||||
char *strDefaultVal;
|
||||
|
||||
|
||||
/*
|
||||
* Ensure we goto the basetype before the domain type.
|
||||
*
|
||||
* Prevents scenarios like the below from failing:
|
||||
* CREATE DOMAIN dom text DEFAULT random();
|
||||
*
|
||||
*/
|
||||
if (typbasetype != InvalidOid) {
|
||||
expr = coerce_type(NULL, expr, typid,
|
||||
typbasetype, atttypmod);
|
||||
/* Convert text datum to C string */
|
||||
strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
|
||||
datum));
|
||||
/* Convert C string to a value of the given type */
|
||||
datum = OidFunctionCall3(type->typinput,
|
||||
CStringGetDatum(strDefaultVal),
|
||||
ObjectIdGetDatum(type->typelem),
|
||||
Int32GetDatum(-1));
|
||||
/* Build a Const node containing the value */
|
||||
expr = (Node *) makeConst(typid,
|
||||
type->typlen,
|
||||
datum,
|
||||
false,
|
||||
type->typbyval,
|
||||
false, /* not a set */
|
||||
false);
|
||||
pfree(strDefaultVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No default */
|
||||
expr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseSysCache(typeTuple);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* getBaseType
|
||||
* If the given type is a domain, return its base type;
|
||||
* otherwise return the type's own OID.
|
||||
*/
|
||||
Oid
|
||||
getBaseType(Oid typid)
|
||||
{
|
||||
/*
|
||||
* We loop to find the bottom base type in a stack of domains.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
HeapTuple tup;
|
||||
Form_pg_type typTup;
|
||||
|
||||
tup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(typid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "getBaseType: failed to lookup type %u", typid);
|
||||
typTup = (Form_pg_type) GETSTRUCT(tup);
|
||||
if (typTup->typtype != 'd')
|
||||
{
|
||||
/* Not a domain, so done */
|
||||
ReleaseSysCache(tup);
|
||||
break;
|
||||
}
|
||||
|
||||
typid = typTup->typbasetype;
|
||||
ReleaseSysCache(tup);
|
||||
}
|
||||
|
||||
return typid;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_typavgwidth
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user