From 97377048b460823a300b1d414203c5f09c8efc1b Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Thu, 18 Jul 2002 23:11:32 +0000 Subject: [PATCH] pg_cast table, and standards-compliant CREATE/DROP CAST commands, plus extension to create binary compatible casts. Includes dependency tracking as well. pg_proc.proimplicit is now defunct, but will be removed in a separate commit. pg_dump provides a migration path from the previous scheme to declare casts. Dumping binary compatible casts is currently impossible, though. --- doc/src/sgml/ref/allfiles.sgml | 4 +- doc/src/sgml/ref/create_cast.sgml | 232 +++++++++++++++++++ doc/src/sgml/ref/create_function.sgml | 72 +----- doc/src/sgml/ref/drop_cast.sgml | 133 +++++++++++ doc/src/sgml/reference.sgml | 4 +- doc/src/sgml/release.sgml | 3 +- src/backend/catalog/Makefile | 4 +- src/backend/catalog/dependency.c | 17 +- src/backend/catalog/indexing.c | 4 +- src/backend/catalog/pg_aggregate.c | 3 +- src/backend/catalog/pg_proc.c | 5 +- src/backend/commands/functioncmds.c | 252 ++++++++++++++++++--- src/backend/parser/gram.y | 138 ++++------- src/backend/parser/keywords.c | 3 +- src/backend/parser/parse_coerce.c | 96 ++++---- src/backend/tcop/postgres.c | 12 +- src/backend/tcop/utility.c | 10 +- src/backend/utils/adt/ruleutils.c | 45 ++-- src/backend/utils/adt/sets.c | 3 +- src/backend/utils/cache/syscache.c | 14 +- src/bin/initdb/initdb.sh | 3 +- src/bin/pg_dump/common.c | 26 ++- src/bin/pg_dump/pg_dump.c | 113 ++++++++- src/bin/pg_dump/pg_dump.h | 5 +- src/include/catalog/catname.h | 3 +- src/include/catalog/catversion.h | 4 +- src/include/catalog/indexing.h | 6 +- src/include/catalog/pg_cast.h | 225 ++++++++++++++++++ src/include/catalog/pg_proc.h | 5 +- src/include/commands/defrem.h | 5 +- src/include/nodes/nodes.h | 4 +- src/include/nodes/parsenodes.h | 28 ++- src/include/utils/syscache.h | 49 ++-- src/test/regress/expected/opr_sanity.out | 59 +++-- src/test/regress/expected/sanity_check.out | 3 +- src/test/regress/sql/opr_sanity.sql | 50 ++-- 36 files changed, 1299 insertions(+), 343 deletions(-) create mode 100644 doc/src/sgml/ref/create_cast.sgml create mode 100644 doc/src/sgml/ref/drop_cast.sgml create mode 100644 src/include/catalog/pg_cast.h diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index c9ece5af56..55b4fc5c9f 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -1,5 +1,5 @@ @@ -51,6 +51,7 @@ Complete list of usable sgml source files in this directory. + @@ -71,6 +72,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/create_cast.sgml b/doc/src/sgml/ref/create_cast.sgml new file mode 100644 index 0000000000..81259949f1 --- /dev/null +++ b/doc/src/sgml/ref/create_cast.sgml @@ -0,0 +1,232 @@ + + + + + CREATE CAST + SQL - Language Statements + + + + CREATE CAST + define a user-defined cast + + + + +CREATE CAST (sourcetype AS targettype) + WITH FUNCTION funcname (argtype) + [AS ASSIGNMENT] + +CREATE CAST (sourcetype AS targettype) + WITHOUT FUNCTION + [AS ASSIGNMENT] + + + + + Description + + + CREATE CAST defines a new cast. A cast + specifies which function can be invoked when a conversion between + two data types is requested. For example, + +SELECT CAST(42 AS text); + + converts the integer constant 42 to type text by + invoking a previously specified function, in this case + text(int4). (If no suitable cast has been defined, the + conversion fails.) + + + + Two types may be binary compatible, which + means that they can be converted into one another for + free without invoking any function. This requires that + corresponding values use the same internal representation. For + instance, the types text and varchar are + binary compatible. + + + + A cast can marked AS ASSIGNMENT, which means that it + can be invoked implicitly in any context where the conversion it + defines is required. Cast functions not so marked can be invoked + only by explicit CAST, + x::typename, or + typename(x) constructs. For + example, supposing that foo.f1 is a column of + type text, then + +INSERT INTO foo(f1) VALUES(42); + + will be allowed if the cast from type integer to type + text is marked AS ASSIGNMENT, otherwise + not. (We generally use the term implicit + cast to describe this kind of cast.) + + + + It is wise to be conservative about marking casts as implicit. An + overabundance of implicit casting paths can cause + PostgreSQL to choose surprising + interpretations of commands, or to be unable to resolve commands at + all because there are multiple possible interpretations. A good + rule of thumb is to make cast implicitly invokable only for + information-preserving transformations between types in the same + general type category. For example, int2 to + int4 casts can reasonably be implicit, but be wary of + marking int4 to text or + float8 to int4 as implicit casts. + + + + To be able to create a cast, you must own the underlying function. + To be able to create a binary compatible cast, you must own both + the source and the target data type. + + + + Parameters + + + sourcetype + + + + The name of the source data type of the cast. + + + + + + targettype + + + + The name of the target data type of the cast. + + + + + + funcname(argtype) + + + + The function used to perform the cast. The function name may + be schema-qualified. If it is not, the function will be looked + up in the path. The argument type must be identical to the + source type, the result data type must match the target type of + the cast. Cast functions must be marked immutable. + + + + + + WITHOUT FUNCTION + + + + Indicates that the source type and the target type are binary + compatible, so no function is required to perform the cast. + + + + + + AS ASSIGNMENT + + + + Indicates that the cast may be invoked implicitly. + + + + + + + + + Notes + + + Use DROP CAST to remove user-defined casts. + + + + The privileges required to create a cast may be changed in a future + release. + + + + Remember that if you want to be able to convert types both ways you + need to declare casts both ways explicitly. + + + + Prior to PostgreSQL 7.3, every function that had the same name as a + data type, returned that data type, and took one argument of a + different type was automatically a cast function. This system has + been abandoned in face of the introduction of schemas and to be + able to store binary compatible casts. The built-in cast functions + still follow this naming scheme, but they have to be declared as + casts explicitly now. + + + + + + Examples + + + To create a cast from type text to type + int using the function int4(text): + +CREATE CAST (text AS int4) WITH FUNCTION int4(text); + + (This cast is already predefined in the system.) + + + + + + Compatibility + + + The CREATE CAST command conforms to SQL99, + except that SQL99 does not make provisions for binary compatible + types. + + + + + + See Also + + + , + , + , + PostgreSQL Programmer's Guide + + + + + + diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml index b2d2314a73..e2170dcc45 100644 --- a/doc/src/sgml/ref/create_function.sgml +++ b/doc/src/sgml/ref/create_function.sgml @@ -1,5 +1,5 @@ @@ -20,7 +20,6 @@ CREATE [ OR REPLACE ] FUNCTION name { LANGUAGE langname | IMMUTABLE | STABLE | VOLATILE | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT - | IMPLICIT CAST | [EXTERNAL] SECURITY INVOKER | [EXTERNAL] SECURITY DEFINER | AS 'definition' | AS 'obj_file', 'link_symbol' @@ -188,18 +187,6 @@ CREATE [ OR REPLACE ] FUNCTION name - - IMPLICIT CAST - - - - Indicates that the function may be used for implicit type - conversions. See for more detail. - - - - EXTERNAL SECURITY INVOKER EXTERNAL SECURITY DEFINER @@ -285,14 +272,6 @@ CREATE [ OR REPLACE ] FUNCTION name - - implicitCoercion - - - Same as IMPLICIT CAST - - - Attribute names are not case-sensitive. @@ -394,55 +373,6 @@ CREATE [ OR REPLACE ] FUNCTION name - - - Type Cast Functions - - - A function that has one argument and is named the same as its return - data type (including the schema name) is considered to be a type - casting function: it can be invoked to convert a value of its input - data type into a value - of its output datatype. For example, - -SELECT CAST(42 AS text); - - converts the integer constant 42 to text by invoking a function - text(int4), if such a function exists and returns type - text. (If no suitable conversion function can be found, the cast fails.) - - - - If a potential cast function is marked IMPLICIT CAST, - then it can be invoked implicitly in any context where the - conversion it defines is required. Cast functions not so marked - can be invoked only by explicit CAST, - x::typename, or - typename(x) constructs. For - example, supposing that foo.f1 is a column of - type text, then - -INSERT INTO foo(f1) VALUES(42); - - will be allowed if text(int4) is marked - IMPLICIT CAST, otherwise not. - - - - It is wise to be conservative about marking cast functions as - implicit casts. An overabundance of implicit casting paths can - cause PostgreSQL to choose surprising - interpretations of commands, or to be unable to resolve commands at - all because there are multiple possible interpretations. A good - rule of thumb is to make cast implicitly invokable only for - information-preserving transformations between types in the same - general type category. For example, int2 to - int4 casts can reasonably be implicit, but be wary of - marking int4 to text or - float8 to int4 as implicit casts. - - - Examples diff --git a/doc/src/sgml/ref/drop_cast.sgml b/doc/src/sgml/ref/drop_cast.sgml new file mode 100644 index 0000000000..37152114bc --- /dev/null +++ b/doc/src/sgml/ref/drop_cast.sgml @@ -0,0 +1,133 @@ + + + + + DROP CAST + SQL - Language Statements + + + + DROP CAST + remove a user-defined cast + + + + +DROP CAST (sourcetype AS targettype) + [ CASCADE | RESTRICT ] + + + + + Description + + + DROP CAST removes a previously defined cast. + + + + To be able to drop a cast, you must own the underlying function. + To be able to drop a binary compatible cast, you must own both the + source and the target data type. These are the same privileges + that are required to create a cast. + + + + Parameters + + + sourcetype + + + + The name of the source data type of the cast. + + + + + + targettype + + + + The name of the target data type of the cast. + + + + + + CASCADE + RESTRICT + + + + These key words do not have any effect, since there are no + dependencies on casts. + + + + + + + + + Notes + + + Use CREATE CAST to create user-defined casts. + + + + The privileges required to drop a cast may be changed in a future + release. + + + + + + Examples + + + To drop the cast from type text to type int: + +DROP CAST (text AS int4); + + + + + + + Compatibility + + + The DROP CAST command conforms to SQL99. + + + + + + See Also + + + + + + + + + diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 8249039826..39fec262dd 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -1,5 +1,5 @@ @@ -60,6 +60,7 @@ PostgreSQL Reference Manual &commit; ©Table; &createAggregate; + &createCast; &createConstraint; &createDatabase; &createDomain; @@ -80,6 +81,7 @@ PostgreSQL Reference Manual &declare; &delete; &dropAggregate; + &dropCast; &dropDatabase; &dropDomain; &dropFunction; diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index f215c84b93..4f7911ff4e 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -1,5 +1,5 @@ @@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without worries about funny characters. --> objectId); break; + case OCLASS_CAST: + DropCastById(object->objectId); + break; + default: elog(ERROR, "doDeletion: Unsupported object class %u", object->classId); @@ -979,6 +984,7 @@ term_object_addresses(ObjectAddresses *addrs) static void init_object_classes(void) { + object_classes[OCLASS_CAST] = get_system_catalog_relid(CastRelationName); object_classes[OCLASS_CLASS] = RelOid_pg_class; object_classes[OCLASS_PROC] = RelOid_pg_proc; object_classes[OCLASS_TYPE] = RelOid_pg_type; @@ -1023,6 +1029,11 @@ getObjectClass(const ObjectAddress *object) if (!object_classes_initialized) init_object_classes(); + if (object->classId == object_classes[OCLASS_CAST]) + { + Assert(object->objectSubId == 0); + return OCLASS_CAST; + } if (object->classId == object_classes[OCLASS_CONSTRAINT]) { Assert(object->objectSubId == 0); @@ -1078,6 +1089,10 @@ getObjectDescription(const ObjectAddress *object) switch (getObjectClass(object)) { + case OCLASS_CAST: + appendStringInfo(&buffer, "cast"); + break; + case OCLASS_CLASS: getRelationDescription(&buffer, object->objectId); if (object->objectSubId != 0) diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 9f16b4d4cc..80e3a15cf5 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.97 2002/07/15 16:33:31 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.98 2002/07/18 23:11:27 petere Exp $ * *------------------------------------------------------------------------- */ @@ -43,6 +43,8 @@ char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeRelidNameIndex, AttributeRelidNumIndex}; char *Name_pg_attrdef_indices[Num_pg_attrdef_indices] = {AttrDefaultIndex, AttrDefaultOidIndex}; +char *Name_pg_cast_indices[Num_pg_cast_indices] = +{CastSourceTargetIndex}; char *Name_pg_class_indices[Num_pg_class_indices] = {ClassNameNspIndex, ClassOidIndex}; char *Name_pg_constraint_indices[Num_pg_constraint_indices] = diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 28d7e83f57..189db5d77d 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.50 2002/07/16 22:12:18 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.51 2002/07/18 23:11:27 petere Exp $ * *------------------------------------------------------------------------- */ @@ -144,7 +144,6 @@ AggregateCreate(const char *aggName, "-", /* probin */ true, /* isAgg */ false, /* security invoker (currently not definable for agg) */ - false, /* isImplicit */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not needed for agg) */ BYTE_PCT, /* default cost values */ diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index f8693fa7fe..29cfbb9b46 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.78 2002/07/18 16:47:23 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.79 2002/07/18 23:11:27 petere Exp $ * *------------------------------------------------------------------------- */ @@ -55,7 +55,6 @@ ProcedureCreate(const char *procedureName, const char *probin, bool isAgg, bool security_definer, - bool isImplicit, bool isStrict, char volatility, int32 byte_pct, @@ -163,7 +162,7 @@ ProcedureCreate(const char *procedureName, values[i++] = ObjectIdGetDatum(languageObjectId); /* prolang */ values[i++] = BoolGetDatum(isAgg); /* proisagg */ values[i++] = BoolGetDatum(security_definer); /* prosecdef */ - values[i++] = BoolGetDatum(isImplicit); /* proimplicit */ + values[i++] = BoolGetDatum(false); /* proimplicit */ values[i++] = BoolGetDatum(isStrict); /* proisstrict */ values[i++] = BoolGetDatum(returnsSet); /* proretset */ values[i++] = CharGetDatum(volatility); /* provolatile */ diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 9a33810b07..2ed9581b66 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.8 2002/07/12 18:43:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.9 2002/07/18 23:11:27 petere Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -34,7 +34,9 @@ #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/dependency.h" +#include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_cast.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" @@ -44,6 +46,7 @@ #include "parser/parse_func.h" #include "parser/parse_type.h" #include "utils/acl.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -171,8 +174,7 @@ compute_attributes_sql_style(const List *options, char **language, char *volatility_p, bool *strict_p, - bool *security_definer, - bool *implicit_cast) + bool *security_definer) { const List *option; DefElem *as_item = NULL; @@ -180,7 +182,6 @@ compute_attributes_sql_style(const List *options, DefElem *volatility_item = NULL; DefElem *strict_item = NULL; DefElem *security_item = NULL; - DefElem *implicit_item = NULL; foreach(option, options) { @@ -216,12 +217,6 @@ compute_attributes_sql_style(const List *options, elog(ERROR, "conflicting or redundant options"); security_item = defel; } - else if (strcmp(defel->defname, "implicit")==0) - { - if (implicit_item) - elog(ERROR, "conflicting or redundant options"); - implicit_item = defel; - } else elog(ERROR, "invalid CREATE FUNCTION option"); } @@ -252,8 +247,6 @@ compute_attributes_sql_style(const List *options, *strict_p = intVal(strict_item->arg); if (security_item) *security_definer = intVal(security_item->arg); - if (implicit_item) - *implicit_cast = intVal(implicit_item->arg); } @@ -264,10 +257,7 @@ compute_attributes_sql_style(const List *options, * These parameters supply optional information about a function. * All have defaults if not specified. * - * Note: currently, only three of these parameters actually do anything: - * - * * isImplicit means the function may be used as an implicit type - * coercion. + * Note: currently, only two of these parameters actually do anything: * * * isStrict means the function should not be called when any NULL * inputs are present; instead a NULL result value should be assumed. @@ -284,7 +274,7 @@ static void compute_attributes_with_style(List *parameters, int32 *byte_pct_p, int32 *perbyte_cpu_p, int32 *percall_cpu_p, int32 *outin_ratio_p, - bool *isImplicit_p, bool *isStrict_p, + bool *isStrict_p, char *volatility_p) { List *pl; @@ -293,9 +283,7 @@ compute_attributes_with_style(List *parameters, { DefElem *param = (DefElem *) lfirst(pl); - if (strcasecmp(param->defname, "implicitcoercion") == 0) - *isImplicit_p = true; - else if (strcasecmp(param->defname, "isstrict") == 0) + if (strcasecmp(param->defname, "isstrict") == 0) *isStrict_p = true; else if (strcasecmp(param->defname, "isimmutable") == 0) *volatility_p = PROVOLATILE_IMMUTABLE; @@ -398,8 +386,7 @@ CreateFunction(CreateFunctionStmt *stmt) perbyte_cpu, percall_cpu, outin_ratio; - bool isImplicit, - isStrict, + bool isStrict, security; char volatility; HeapTuple languageTuple; @@ -420,14 +407,13 @@ CreateFunction(CreateFunctionStmt *stmt) perbyte_cpu = PERBYTE_CPU; percall_cpu = PERCALL_CPU; outin_ratio = OUTIN_RATIO; - isImplicit = false; isStrict = false; security = false; volatility = PROVOLATILE_VOLATILE; /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, - &as_clause, &language, &volatility, &isStrict, &security, &isImplicit); + &as_clause, &language, &volatility, &isStrict, &security); /* Convert language name to canonical case */ case_translate_language_name(language, languageName); @@ -474,8 +460,7 @@ CreateFunction(CreateFunctionStmt *stmt) compute_attributes_with_style(stmt->withClause, &byte_pct, &perbyte_cpu, &percall_cpu, - &outin_ratio, &isImplicit, &isStrict, - &volatility); + &outin_ratio, &isStrict, &volatility); interpret_AS_clause(languageOid, languageName, as_clause, &prosrc_str, &probin_str); @@ -517,7 +502,6 @@ CreateFunction(CreateFunctionStmt *stmt) probin_str, /* converted to text later */ false, /* not an aggregate */ security, - isImplicit, isStrict, volatility, byte_pct, @@ -639,3 +623,217 @@ RemoveFunctionById(Oid funcOid) heap_close(relation, RowExclusiveLock); } } + + + +/* + * CREATE CAST + */ +void +CreateCast(CreateCastStmt *stmt) +{ + Oid sourcetypeid; + Oid targettypeid; + Oid funcid; + HeapTuple tuple; + Relation relation; + Form_pg_proc procstruct; + + Datum values[Natts_pg_proc]; + char nulls[Natts_pg_proc]; + int i; + + ObjectAddress myself, + referenced; + + sourcetypeid = LookupTypeName(stmt->sourcetype); + if (!OidIsValid(sourcetypeid)) + elog(ERROR, "source data type %s does not exist", + TypeNameToString(stmt->sourcetype)); + + targettypeid = LookupTypeName(stmt->targettype); + if (!OidIsValid(targettypeid)) + elog(ERROR, "target data type %s does not exist", + TypeNameToString(stmt->targettype)); + + if (sourcetypeid == targettypeid) + elog(ERROR, "source data type and target data type are the same"); + + relation = heap_openr(CastRelationName, RowExclusiveLock); + + tuple = SearchSysCache(CASTSOURCETARGET, + ObjectIdGetDatum(sourcetypeid), + ObjectIdGetDatum(targettypeid), + 0, 0); + if (HeapTupleIsValid(tuple)) + elog(ERROR, "cast from data type %s to data type %s already exists", + TypeNameToString(stmt->sourcetype), + TypeNameToString(stmt->targettype)); + + if (stmt->func != NULL) + { + funcid = LookupFuncNameTypeNames(stmt->func->funcname, stmt->func->funcargs, false, "CreateCast"); + + if(!pg_proc_ownercheck(funcid, GetUserId())) + elog(ERROR, "permission denied"); + + tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup of function %u failed", funcid); + + procstruct = (Form_pg_proc) GETSTRUCT(tuple); + if (procstruct->pronargs != 1) + elog(ERROR, "cast function must take 1 argument"); + if (procstruct->proargtypes[0] != sourcetypeid) + elog(ERROR, "argument of cast function must match source data type"); + if (procstruct->prorettype != targettypeid) + elog(ERROR, "return data type of cast function must match target data type"); + if (procstruct->provolatile != PROVOLATILE_IMMUTABLE) + elog(ERROR, "cast function must be immutable"); + if (procstruct->proisagg) + elog(ERROR, "cast function must not be an aggregate function"); + if (procstruct->proretset) + elog(ERROR, "cast function must be not return a set"); + + ReleaseSysCache(tuple); + } + else + { + /* indicates binary compatibility */ + if (!pg_type_ownercheck(sourcetypeid, GetUserId()) + || !pg_type_ownercheck(targettypeid, GetUserId())) + elog(ERROR, "permission denied"); + funcid = 0; + } + + /* ready to go */ + values[Anum_pg_cast_castsource-1] = ObjectIdGetDatum(sourcetypeid); + values[Anum_pg_cast_casttarget-1] = ObjectIdGetDatum(targettypeid); + values[Anum_pg_cast_castfunc-1] = ObjectIdGetDatum(funcid); + values[Anum_pg_cast_castimplicit-1] = BoolGetDatum(stmt->implicit); + + for (i = 0; i < Natts_pg_cast; ++i) + nulls[i] = ' '; + + tuple = heap_formtuple(RelationGetDescr(relation), values, nulls); + simple_heap_insert(relation, tuple); + + if (RelationGetForm(relation)->relhasindex) + { + Relation idescs[Num_pg_cast_indices]; + + CatalogOpenIndices(Num_pg_cast_indices, Name_pg_cast_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_cast_indices, relation, tuple); + CatalogCloseIndices(Num_pg_cast_indices, idescs); + } + + myself.classId = get_system_catalog_relid(CastRelationName); + myself.objectId = tuple->t_data->t_oid; + myself.objectSubId = 0; + + /* dependency on source type */ + referenced.classId = RelOid_pg_type; + referenced.objectId = sourcetypeid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependency on target type */ + referenced.classId = RelOid_pg_type; + referenced.objectId = targettypeid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* dependency on function */ + if (OidIsValid(funcid)) + { + referenced.classId = RelOid_pg_proc; + referenced.objectId = funcid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + heap_freetuple(tuple); + heap_close(relation, RowExclusiveLock); +} + + + +/* + * DROP CAST + */ +void +DropCast(DropCastStmt *stmt) +{ + Oid sourcetypeid; + Oid targettypeid; + HeapTuple tuple; + Form_pg_cast caststruct; + ObjectAddress object; + + sourcetypeid = LookupTypeName(stmt->sourcetype); + if (!OidIsValid(sourcetypeid)) + elog(ERROR, "source data type %s does not exist", + TypeNameToString(stmt->sourcetype)); + + targettypeid = LookupTypeName(stmt->targettype); + if (!OidIsValid(targettypeid)) + elog(ERROR, "target data type %s does not exist", + TypeNameToString(stmt->targettype)); + + tuple = SearchSysCache(CASTSOURCETARGET, + ObjectIdGetDatum(sourcetypeid), + ObjectIdGetDatum(targettypeid), + 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cast from type %s to type %s does not exist", + TypeNameToString(stmt->sourcetype), + TypeNameToString(stmt->targettype)); + + /* Permission check */ + caststruct = (Form_pg_cast) GETSTRUCT(tuple); + if (caststruct->castfunc != InvalidOid) + { + if(!pg_proc_ownercheck(caststruct->castfunc, GetUserId())) + elog(ERROR, "permission denied"); + } + else + { + if (!pg_type_ownercheck(sourcetypeid, GetUserId()) + || !pg_type_ownercheck(targettypeid, GetUserId())) + elog(ERROR, "permission denied"); + } + + ReleaseSysCache(tuple); + + /* + * Do the deletion + */ + object.classId = get_system_catalog_relid(CastRelationName); + object.objectId = tuple->t_data->t_oid; + object.objectSubId = 0; + + performDeletion(&object, stmt->behavior); +} + + +void +DropCastById(Oid castOid) +{ + Relation relation; + ScanKeyData scankey; + HeapScanDesc scan; + HeapTuple tuple; + + relation = heap_openr(CastRelationName, RowExclusiveLock); + ScanKeyEntryInitialize(&scankey, 0x0, + ObjectIdAttributeNumber, F_OIDEQ, + ObjectIdGetDatum(castOid)); + scan = heap_beginscan(relation, SnapshotNow, 1, &scankey); + tuple = heap_getnext(scan, ForwardScanDirection); + if (HeapTupleIsValid(tuple)) + simple_heap_delete(relation, &tuple->t_self); + else + elog(ERROR, "could not find tuple for cast %u", castOid); + heap_endscan(scan); + heap_close(relation, RowExclusiveLock); +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index bece9e0f18..4bc561f96a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.346 2002/07/18 17:14:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.347 2002/07/18 23:11:27 petere Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -135,13 +135,13 @@ static void doNegateFloat(Value *v); AlterDatabaseSetStmt, AlterGroupStmt, AlterTableStmt, AlterUserStmt, AlterUserSetStmt, AnalyzeStmt, ClosePortalStmt, ClusterStmt, CommentStmt, - ConstraintsSetStmt, CopyStmt, CreateAsStmt, + ConstraintsSetStmt, CopyStmt, CreateAsStmt, CreateCastStmt, CreateDomainStmt, CreateGroupStmt, CreatePLangStmt, CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateAssertStmt, CreateTrigStmt, CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt, DropGroupStmt, DropPLangStmt, DropStmt, - DropAssertStmt, DropTrigStmt, DropRuleStmt, + DropAssertStmt, DropTrigStmt, DropRuleStmt, DropCastStmt, DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt, GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt, NotifyStmt, OptimizableStmt, @@ -165,7 +165,7 @@ static void doNegateFloat(Value *v); %type createdb_opt_item, copy_opt_item %type opt_lock, lock_type -%type opt_force, opt_or_replace +%type opt_force, opt_or_replace, opt_assignment %type user_list @@ -346,7 +346,7 @@ static void doNegateFloat(Value *v); HANDLER, HAVING, HOUR_P, - ILIKE, IMMEDIATE, IMMUTABLE, IMPLICIT, IN_P, INCREMENT, + ILIKE, IMMEDIATE, IMMUTABLE, IN_P, INCREMENT, INDEX, INHERITS, INITIALLY, INNER_P, INOUT, INPUT, INSENSITIVE, INSERT, INSTEAD, INT, INTEGER, INTERSECT, INTERVAL, INTO, INVOKER, IS, ISNULL, ISOLATION, @@ -475,6 +475,7 @@ stmt : | CopyStmt | CreateStmt | CreateAsStmt + | CreateCastStmt | CreateDomainStmt | CreateFunctionStmt | CreateSchemaStmt @@ -489,6 +490,7 @@ stmt : | DropStmt | TruncateStmt | CommentStmt + | DropCastStmt | DropGroupStmt | DropPLangStmt | DropAssertStmt @@ -2886,15 +2888,6 @@ RecipeStmt: EXECUTE RECIPE recipe_name * as * language [with parameters] * - * CAST() form allowing all options from the CREATE FUNCTION form: - * create [or replace] cast ( as ) - * as - * language [with parameters] - * - * SQL99 CAST() form (requires a function to be previously defined): - * create [or replace] cast ( as ) - * with function fname () [as assignment] - * *****************************************************************************/ CreateFunctionStmt: @@ -2910,63 +2903,6 @@ CreateFunctionStmt: n->withClause = $9; $$ = (Node *)n; } - /* CREATE CAST SQL99 standard form */ - | CREATE opt_or_replace CAST '(' func_type AS func_type ')' - WITH FUNCTION func_name func_args opt_assignment opt_definition - { - CreateFunctionStmt *n; - char buf[256]; - n = makeNode(CreateFunctionStmt); - n->replace = $2; - n->funcname = $7->names; - n->argTypes = makeList1($5); - n->returnType = $7; - /* expand this into a string of SQL language */ - strcpy(buf, "select "); - strcat(buf, ((Value *)lfirst($11))->val.str); - strcat(buf, "($1)"); - n->options = lappend($14, makeDefElem("as", (Node *)makeList1(makeString(pstrdup(buf))))); - /* make sure that this will allow implicit casting */ - n->options = lappend(n->options, - makeDefElem("implicit", (Node *)makeInteger(TRUE))); - /* and mention that this is SQL language */ - n->options = lappend(n->options, - makeDefElem("language", (Node *)makeString(pstrdup("sql")))); - $$ = (Node *)n; - } - /* CREATE CAST SQL99 minimally variant form */ - | CREATE opt_or_replace CAST '(' func_type AS func_type ')' - WITH FUNCTION func_name func_args AS Sconst opt_definition - { - CreateFunctionStmt *n; - n = makeNode(CreateFunctionStmt); - n->replace = $2; - n->funcname = $7->names; - n->argTypes = makeList1($5); - n->returnType = $7; - n->options = lappend($15, makeDefElem("as", (Node *)lcons(makeList1(makeString($14)), $11))); - /* make sure that this will allow implicit casting */ - n->options = lappend(n->options, - makeDefElem("implicit", (Node *)makeInteger(TRUE))); - n->options = lappend(n->options, - makeDefElem("language", (Node *)makeString(pstrdup("c")))); - $$ = (Node *)n; - } - /* CREATE CAST with mostly CREATE FUNCTION clauses */ - | CREATE opt_or_replace CAST '(' func_type AS func_type ')' - createfunc_opt_list opt_definition - { - CreateFunctionStmt *n; - n = makeNode(CreateFunctionStmt); - n->replace = $2; - n->funcname = $7->names; - n->argTypes = makeList1($5); - n->returnType = $7; - /* make sure that this will allow implicit casting */ - n->options = lappend($9, makeDefElem("implicit", (Node *)makeInteger(TRUE))); - n->withClause = $10; - $$ = (Node *)n; - } ; opt_or_replace: @@ -3090,10 +3026,6 @@ createfunc_opt_item: { $$ = makeDefElem("security", (Node *)makeInteger(FALSE)); } - | IMPLICIT CAST - { - $$ = makeDefElem("implicit", (Node *)makeInteger(TRUE)); - } ; func_as: Sconst { $$ = makeList1(makeString($1)); } @@ -3108,10 +3040,6 @@ opt_definition: | /*EMPTY*/ { $$ = NIL; } ; -opt_assignment: AS ASSIGNMENT {} - | /*EMPTY*/ {} - ; - /***************************************************************************** * @@ -3132,14 +3060,6 @@ RemoveFuncStmt: n->behavior = $5; $$ = (Node *)n; } - | DROP CAST '(' func_type AS func_type ')' opt_drop_behavior - { - RemoveFuncStmt *n = makeNode(RemoveFuncStmt); - n->funcname = $6->names; - n->args = makeList1($4); - n->behavior = $8; - $$ = (Node *)n; - } ; RemoveAggrStmt: @@ -3190,6 +3110,49 @@ any_operator: ; +/***************************************************************************** + * + * CREATE CAST / DROP CAST + * + *****************************************************************************/ + +CreateCastStmt: CREATE CAST '(' ConstTypename AS ConstTypename ')' + WITH FUNCTION function_with_argtypes opt_assignment + { + CreateCastStmt *n = makeNode(CreateCastStmt); + n->sourcetype = $4; + n->targettype = $6; + n->func = (FuncWithArgs *) $10; + n->implicit = $11; + $$ = (Node *)n; + } + | CREATE CAST '(' ConstTypename AS ConstTypename ')' + WITHOUT FUNCTION opt_assignment + { + CreateCastStmt *n = makeNode(CreateCastStmt); + n->sourcetype = $4; + n->targettype = $6; + n->func = NULL; + n->implicit = $10; + $$ = (Node *)n; + } + +opt_assignment: AS ASSIGNMENT { $$ = TRUE; } + | /*EMPTY*/ { $$ = FALSE; } + ; + + +DropCastStmt: DROP CAST '(' ConstTypename AS ConstTypename ')' opt_drop_behavior + { + DropCastStmt *n = makeNode(DropCastStmt); + n->sourcetype = $4; + n->targettype = $6; + n->behavior = $8; + $$ = (Node *)n; + } + + + /***************************************************************************** * * QUERY: @@ -6701,7 +6664,6 @@ unreserved_keyword: | HOUR_P | IMMEDIATE | IMMUTABLE - | IMPLICIT | INCREMENT | INDEX | INHERITS diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index f007092f66..02c9fcdda9 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.121 2002/07/18 17:14:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.122 2002/07/18 23:11:28 petere Exp $ * *------------------------------------------------------------------------- */ @@ -142,7 +142,6 @@ static const ScanKeyword ScanKeywords[] = { {"ilike", ILIKE}, {"immediate", IMMEDIATE}, {"immutable", IMMUTABLE}, - {"implicit", IMPLICIT}, {"in", IN_P}, {"increment", INCREMENT}, {"index", INDEX}, diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index fda9100d67..15896a37d7 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,12 +8,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.77 2002/07/09 13:52:14 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.78 2002/07/18 23:11:28 petere Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_cast.h" #include "catalog/pg_proc.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" @@ -31,8 +32,9 @@ 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, bool isExplicit); +static Oid find_coercion_function(Oid targetTypeId, Oid sourceTypeId, + bool isExplicit); +static Oid find_typmod_coercion_function(Oid typeId); static Node *TypeConstraints(Node *arg, Oid typeId); /* coerce_type() @@ -142,7 +144,6 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, funcId = find_coercion_function(baseTypeId, getBaseType(inputTypeId), - InvalidOid, isExplicit); if (!OidIsValid(funcId)) elog(ERROR, "coerce_type: no conversion function from '%s' to '%s'", @@ -258,7 +259,6 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, */ funcId = find_coercion_function(getBaseType(targetTypeId), getBaseType(inputTypeId), - InvalidOid, isExplicit); if (!OidIsValid(funcId)) return false; @@ -312,8 +312,7 @@ coerce_type_typmod(ParseState *pstate, Node *node, if (atttypmod < 0 || atttypmod == exprTypmod(node)) return node; - /* Note this is always implicit coercion */ - funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID, false); + funcId = find_typmod_coercion_function(baseTypeId); if (OidIsValid(funcId)) { Const *cons; @@ -621,21 +620,25 @@ TypeCategory(Oid inType) static bool DirectlyBinaryCompatible(Oid type1, Oid type2) { + HeapTuple tuple; + bool result; + if (type1 == type2) return true; - if (TypeIsTextGroup(type1) && TypeIsTextGroup(type2)) - return true; - if (TypeIsInt4GroupA(type1) && TypeIsInt4GroupA(type2)) - return true; - if (TypeIsInt4GroupB(type1) && TypeIsInt4GroupB(type2)) - return true; - if (TypeIsInt4GroupC(type1) && TypeIsInt4GroupC(type2)) - return true; - if (TypeIsInetGroup(type1) && TypeIsInetGroup(type2)) - return true; - if (TypeIsBitGroup(type1) && TypeIsBitGroup(type2)) - return true; - return false; + + tuple = SearchSysCache(CASTSOURCETARGET, type1, type2, 0, 0); + if (HeapTupleIsValid(tuple)) + { + Form_pg_cast caststruct; + + caststruct = (Form_pg_cast) GETSTRUCT(tuple); + result = caststruct->castfunc == InvalidOid && caststruct->castimplicit; + ReleaseSysCache(tuple); + } + else + result = false; + + return result; } @@ -750,34 +753,51 @@ PreferredType(CATEGORY category, Oid type) * If a function is found, return its pg_proc OID; else return InvalidOid. */ static Oid -find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType, - bool isExplicit) +find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit) +{ + Oid funcid = InvalidOid; + HeapTuple tuple; + + tuple = SearchSysCache(CASTSOURCETARGET, + ObjectIdGetDatum(sourceTypeId), + ObjectIdGetDatum(targetTypeId), + 0, 0); + + if (HeapTupleIsValid(tuple)) + { + Form_pg_cast cform = (Form_pg_cast) GETSTRUCT(tuple); + + if (isExplicit || cform->castimplicit) + funcid = cform->castfunc; + + ReleaseSysCache(tuple); + } + + return funcid; +} + + +static Oid +find_typmod_coercion_function(Oid typeId) { Oid funcid = InvalidOid; Type targetType; char *typname; Oid typnamespace; Oid oid_array[FUNC_MAX_ARGS]; - int nargs; HeapTuple ftup; - targetType = typeidType(targetTypeId); + targetType = typeidType(typeId); typname = NameStr(((Form_pg_type) GETSTRUCT(targetType))->typname); typnamespace = ((Form_pg_type) GETSTRUCT(targetType))->typnamespace; 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; + oid_array[0] = typeId; + oid_array[1] = INT4OID; ftup = SearchSysCache(PROCNAMENSP, CStringGetDatum(typname), - Int16GetDatum(nargs), + Int16GetDatum(2), PointerGetDatum(oid_array), ObjectIdGetDatum(typnamespace)); if (HeapTupleIsValid(ftup)) @@ -785,15 +805,11 @@ find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType, Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup); /* Make sure the function's result type is as expected */ - if (pform->prorettype == targetTypeId && !pform->proretset && + if (pform->prorettype == typeId && !pform->proretset && !pform->proisagg) { - /* If needed, make sure it can be invoked implicitly */ - if (isExplicit || pform->proimplicit) - { - /* Okay to use it */ - funcid = ftup->t_data->t_oid; - } + /* Okay to use it */ + funcid = ftup->t_data->t_oid; } ReleaseSysCache(ftup); } diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 862faf34ca..d913736833 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.271 2002/07/18 16:47:25 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.272 2002/07/18 23:11:28 petere Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -1693,7 +1693,7 @@ PostgresMain(int argc, char *argv[], const char *username) if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.271 $ $Date: 2002/07/18 16:47:25 $\n"); + puts("$Revision: 1.272 $ $Date: 2002/07/18 23:11:28 $\n"); } /* @@ -2444,6 +2444,14 @@ CreateCommandTag(Node *parsetree) tag = "CREATE CONVERSION"; break; + case T_CreateCastStmt: + tag = "CREATE CAST"; + break; + + case T_DropCastStmt: + tag = "DROP CAST"; + break; + default: elog(LOG, "CreateCommandTag: unknown parse node type %d", nodeTag(parsetree)); diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 8ba7466ee2..54eeab77cd 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.163 2002/07/18 16:47:25 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.164 2002/07/18 23:11:28 petere Exp $ * *------------------------------------------------------------------------- */ @@ -829,6 +829,14 @@ ProcessUtility(Node *parsetree, } break; + case T_CreateCastStmt: + CreateCast((CreateCastStmt *) parsetree); + break; + + case T_DropCastStmt: + DropCast((DropCastStmt *) parsetree); + break; + default: elog(ERROR, "ProcessUtility: command #%d unsupported", nodeTag(parsetree)); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 2772b66873..5999ad9628 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.111 2002/07/18 17:14:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.112 2002/07/18 23:11:28 petere Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -43,6 +43,7 @@ #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/namespace.h" +#include "catalog/pg_cast.h" #include "catalog/pg_index.h" #include "catalog/pg_opclass.h" #include "catalog/pg_operator.h" @@ -2048,9 +2049,9 @@ get_agg_expr(Aggref *aggref, deparse_context *context) * Strip any type coercions at the top of the given expression tree, * as long as they are coercions to the given datatype. * - * A RelabelType node is always a type coercion. A function call is also - * considered a type coercion if it has one argument and the function name - * is the same as the (internal) name of its result type. + * A RelabelType node is always a type coercion. A function call is + * also considered a type coercion if it has one argument and there is + * a cast declared that uses it. * * XXX It'd be better if the parsetree retained some explicit indication * of the coercion, so we didn't need these heuristics. @@ -2069,9 +2070,9 @@ strip_type_coercion(Node *expr, Oid resultType) { Func *func; HeapTuple procTuple; - HeapTuple typeTuple; + HeapTuple castTuple; Form_pg_proc procStruct; - Form_pg_type typeStruct; + Form_pg_cast castStruct; func = (Func *) (((Expr *) expr)->oper); Assert(IsA(func, Func)); @@ -2085,33 +2086,33 @@ strip_type_coercion(Node *expr, Oid resultType) elog(ERROR, "cache lookup for proc %u failed", func->funcid); procStruct = (Form_pg_proc) GETSTRUCT(procTuple); /* Double-check func has one arg and correct result type */ - /* Also, it must be an implicit coercion function */ if (procStruct->pronargs != 1 || - procStruct->prorettype != resultType || - !procStruct->proimplicit) + procStruct->prorettype != resultType) { ReleaseSysCache(procTuple); return expr; } - /* See if function has same name/namespace as its result type */ - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(procStruct->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "cache lookup for type %u failed", - procStruct->prorettype); - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - if (strcmp(NameStr(procStruct->proname), - NameStr(typeStruct->typname)) != 0 || - procStruct->pronamespace != typeStruct->typnamespace) + /* See if function has is actually declared as a cast */ + castTuple = SearchSysCache(CASTSOURCETARGET, + ObjectIdGetDatum(procStruct->proargtypes[0]), + ObjectIdGetDatum(procStruct->prorettype), + 0, 0); + if (!HeapTupleIsValid(castTuple)) + { + ReleaseSysCache(procTuple); + return expr; + } + /* It must also be an implicit cast. */ + castStruct = (Form_pg_cast) GETSTRUCT(castTuple); + if (!castStruct->castimplicit) { ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); + ReleaseSysCache(castTuple); return expr; } /* Okay, it is indeed a type-coercion function */ ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); + ReleaseSysCache(castTuple); return strip_type_coercion(lfirst(((Expr *) expr)->args), resultType); } diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c index 52763d29e4..d03fd88e4d 100644 --- a/src/backend/utils/adt/sets.c +++ b/src/backend/utils/adt/sets.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.46 2002/06/20 20:29:38 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.47 2002/07/18 23:11:29 petere Exp $ * *------------------------------------------------------------------------- */ @@ -63,7 +63,6 @@ SetDefine(char *querystr, Oid elemType) fileName, /* probin */ false, /* not aggregate */ false, /* security invoker */ - false, /* not implicit coercion */ false, /* isStrict (irrelevant, no args) */ PROVOLATILE_VOLATILE, /* assume unsafe */ 100, /* byte_pct */ diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index a724a0874e..5f0be16b75 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.81 2002/07/11 07:39:27 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.82 2002/07/18 23:11:29 petere Exp $ * * NOTES * These routines allow the parser/planner/executor to perform @@ -28,6 +28,7 @@ #include "catalog/pg_aggregate.h" #include "catalog/pg_amop.h" #include "catalog/pg_amproc.h" +#include "catalog/pg_cast.h" #include "catalog/pg_conversion.h" #include "catalog/pg_group.h" #include "catalog/pg_index.h" @@ -174,6 +175,17 @@ static const struct cachedesc cacheinfo[] = { 0, 0 }}, + { + CastRelationName, /* CASTSOURCETARGET */ + CastSourceTargetIndex, + 0, + 2, + { + Anum_pg_cast_castsource, + Anum_pg_cast_casttarget, + 0, + 0 + }}, {OperatorClassRelationName, /* CLAAMNAMENSP */ OpclassAmNameNspIndex, 0, diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index 9902a24fc4..f582da72c3 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.160 2002/07/18 16:47:25 tgl Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.161 2002/07/18 23:11:29 petere Exp $ # #------------------------------------------------------------------------- @@ -708,6 +708,7 @@ $ECHO_N "initializing pg_depend... "$ECHO_C -- First delete any already-made entries; PINs override all else, and must -- be the only entries for their objects. DELETE FROM pg_depend; +INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_cast; INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_class; INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_proc; INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' FROM pg_type; diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 827459b4db..123ef3df05 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.65 2002/06/20 20:29:41 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.66 2002/07/18 23:11:29 petere Exp $ * *------------------------------------------------------------------------- */ @@ -170,6 +170,13 @@ dumpSchema(Archive *fout, dumpOprs(fout, oprinfo, numOperators); } + if (!dataOnly) + { + if (g_verbose) + write_msg(NULL, "dumping out user-defined casts\n"); + dumpCasts(fout, finfo, numFuncs, tinfo, numTypes); + } + *numTablesPtr = numTables; return tblinfo; } @@ -386,6 +393,23 @@ findFuncByOid(FuncInfo *finfo, int numFuncs, const char *oid) return -1; } +/* + * Finds the index (in tinfo) of the type with the given OID. Returns + * -1 if not found. + */ +int +findTypeByOid(TypeInfo *tinfo, int numTypes, const char *oid) +{ + int i; + + for (i = 0; i < numTypes; i++) + { + if (strcmp(tinfo[i].oid, oid) == 0) + return i; + } + return -1; +} + /* * findOprByOid * given the oid of an operator, return the name of the operator diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 1aefb9801a..a173839b4d 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.273 2002/07/18 04:50:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.274 2002/07/18 23:11:29 petere Exp $ * *------------------------------------------------------------------------- */ @@ -3398,7 +3398,6 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo) char *prosrc; char *probin; char *provolatile; - char *proimplicit; char *proisstrict; char *prosecdef; char *lanname; @@ -3417,7 +3416,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo) { appendPQExpBuffer(query, "SELECT proretset, prosrc, probin, " - "provolatile, proimplicit, proisstrict, prosecdef, " + "provolatile, proisstrict, prosecdef, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname " "FROM pg_catalog.pg_proc " "WHERE oid = '%s'::pg_catalog.oid", @@ -3428,7 +3427,6 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo) appendPQExpBuffer(query, "SELECT proretset, prosrc, probin, " "case when proiscachable then 'i' else 'v' end as provolatile, " - "'f'::boolean as proimplicit, " "proisstrict, " "'f'::boolean as prosecdef, " "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname " @@ -3441,7 +3439,6 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo) appendPQExpBuffer(query, "SELECT proretset, prosrc, probin, " "case when proiscachable then 'i' else 'v' end as provolatile, " - "'f'::boolean as proimplicit, " "'f'::boolean as proisstrict, " "'f'::boolean as prosecdef, " "(SELECT lanname FROM pg_language WHERE oid = prolang) as lanname " @@ -3472,7 +3469,6 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo) prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc")); probin = PQgetvalue(res, 0, PQfnumber(res, "probin")); provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile")); - proimplicit = PQgetvalue(res, 0, PQfnumber(res, "proimplicit")); proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict")); prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef")); lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); @@ -3533,9 +3529,6 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo) } } - if (proimplicit[0] == 't') - appendPQExpBuffer(q, " IMPLICIT CAST"); - if (proisstrict[0] == 't') appendPQExpBuffer(q, " STRICT"); @@ -3569,6 +3562,108 @@ done: free(funcsig_tag); } + +/* + * Dump all casts + */ +void +dumpCasts(Archive *fout, + FuncInfo *finfo, int numFuncs, + TypeInfo *tinfo, int numTypes) +{ + PGresult *res; + PQExpBuffer query = createPQExpBuffer(); + PQExpBuffer defqry = createPQExpBuffer(); + PQExpBuffer delqry = createPQExpBuffer(); + int ntups; + int i; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + if (fout->remoteVersion >= 70300) + appendPQExpBuffer(query, "SELECT oid, castsource, casttarget, castfunc, castimplicit FROM pg_cast ORDER BY 1,2,3;"); + else + appendPQExpBuffer(query, "SELECT p.oid, t1.oid, t2.oid, p.oid, true FROM pg_type t1, pg_type t2, pg_proc p WHERE p.pronargs = 1 AND p.proargtypes[0] = t1.oid AND p.prorettype = t2.oid AND p.proname = t2.typname ORDER BY 1,2,3;"); + + res = PQexec(g_conn, query->data); + if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) + { + write_msg(NULL, "query to obtain list of casts failed: %s", + PQerrorMessage(g_conn)); + exit_nicely(); + } + ntups = PQntuples(res); + + for (i = 0; i < ntups; i++) + { + char * castoid = PQgetvalue(res, i, 0); + char * castsource = PQgetvalue(res, i, 1); + char * casttarget = PQgetvalue(res, i, 2); + char * castfunc = PQgetvalue(res, i, 3); + char * castimplicit = PQgetvalue(res, i, 4); + int fidx = -1; + const char *((*deps)[]); + + if (strcmp(castfunc, "0") != 0) + fidx = findFuncByOid(finfo, numFuncs, castfunc); + + /* + * We treat the cast as being in the namespace of the + * underlying function. This doesn't handle binary compatible + * casts. Where should those go? + */ + if (fidx < 0 || !finfo[fidx].pronamespace->dump) + continue; + + /* Make a dependency to ensure function is dumped first */ + if (fidx >= 0) + { + deps = malloc(sizeof(char *) * 2); + + (*deps)[0] = strdup(castfunc); + (*deps)[1] = NULL; /* End of List */ + } + else + deps = NULL; + + resetPQExpBuffer(defqry); + resetPQExpBuffer(delqry); + + appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n", + getFormattedTypeName(castsource, zeroAsNone), + getFormattedTypeName(casttarget, zeroAsNone)); + + appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ", + getFormattedTypeName(castsource, zeroAsNone), + getFormattedTypeName(casttarget, zeroAsNone)); + + if (strcmp(castfunc, "0")==0) + appendPQExpBuffer(defqry, "WITHOUT FUNCTION"); + else + appendPQExpBuffer(defqry, "WITH FUNCTION %s", + format_function_signature(&finfo[fidx], true)); + + if (strcmp(castimplicit, "t")==0) + appendPQExpBuffer(defqry, " AS ASSIGNMENT"); + appendPQExpBuffer(defqry, ";\n"); + + ArchiveEntry(fout, castoid, + format_function_signature(&finfo[fidx], false), + finfo[fidx].pronamespace->nspname, "", + "CAST", deps, + defqry->data, delqry->data, + NULL, NULL, NULL); + } + + PQclear(res); + + destroyPQExpBuffer(query); + destroyPQExpBuffer(defqry); + destroyPQExpBuffer(delqry); +} + + /* * dumpOprs * writes out to fout the queries to recreate all the user-defined operators diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 17491b027e..732fdfb74e 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_dump.h,v 1.90 2002/07/06 20:12:30 momjian Exp $ + * $Id: pg_dump.h,v 1.91 2002/07/18 23:11:29 petere Exp $ * *------------------------------------------------------------------------- */ @@ -177,6 +177,7 @@ typedef enum _OidOptions extern int findTableByOid(TableInfo *tbinfo, int numTables, const char *oid); extern char *findOprByOid(OprInfo *oprinfo, int numOprs, const char *oid); extern int findFuncByOid(FuncInfo *finfo, int numFuncs, const char *oid); +extern int findTypeByOid(TypeInfo *tinfo, int numTypes, const char *oid); extern void check_conn_and_db(void); extern void exit_nicely(void); @@ -202,6 +203,8 @@ extern void dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs, TypeInfo *tinfo, int numTypes); extern void dumpProcLangs(Archive *fout, FuncInfo finfo[], int numFuncs); extern void dumpFuncs(Archive *fout, FuncInfo finfo[], int numFuncs); +extern void dumpCasts(Archive *fout, FuncInfo *finfo, int numFuncs, + TypeInfo *tinfo, int numTypes); extern void dumpAggs(Archive *fout, AggInfo agginfo[], int numAggregates); extern void dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators); extern void dumpTables(Archive *fout, TableInfo tblinfo[], int numTables, diff --git a/src/include/catalog/catname.h b/src/include/catalog/catname.h index 0e452a9ca3..dc1fedae9e 100644 --- a/src/include/catalog/catname.h +++ b/src/include/catalog/catname.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catname.h,v 1.28 2002/07/12 18:43:19 tgl Exp $ + * $Id: catname.h,v 1.29 2002/07/18 23:11:30 petere Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #define AccessMethodOperatorRelationName "pg_amop" #define AccessMethodProcedureRelationName "pg_amproc" #define AttributeRelationName "pg_attribute" +#define CastRelationName "pg_cast" #define ConstraintRelationName "pg_constraint" #define ConversionRelationName "pg_conversion" #define DatabaseRelationName "pg_database" diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 5fd581b254..077da864a4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.140 2002/07/15 16:33:31 tgl Exp $ + * $Id: catversion.h,v 1.141 2002/07/18 23:11:30 petere Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200207141 +#define CATALOG_VERSION_NO 200207191 #endif diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 91e3593447..777a4031dd 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: indexing.h,v 1.70 2002/07/15 16:33:31 tgl Exp $ + * $Id: indexing.h,v 1.71 2002/07/18 23:11:30 petere Exp $ * *------------------------------------------------------------------------- */ @@ -26,6 +26,7 @@ #define Num_pg_amproc_indices 1 #define Num_pg_attr_indices 2 #define Num_pg_attrdef_indices 2 +#define Num_pg_cast_indices 1 #define Num_pg_class_indices 2 #define Num_pg_constraint_indices 3 #define Num_pg_conversion_indices 3 @@ -60,6 +61,7 @@ #define AttrDefaultOidIndex "pg_attrdef_oid_index" #define AttributeRelidNameIndex "pg_attribute_relid_attnam_index" #define AttributeRelidNumIndex "pg_attribute_relid_attnum_index" +#define CastSourceTargetIndex "pg_cast_source_target_index" #define ClassNameNspIndex "pg_class_relname_nsp_index" #define ClassOidIndex "pg_class_oid_index" #define ConstraintNameNspIndex "pg_constraint_conname_nsp_index" @@ -108,6 +110,7 @@ extern char *Name_pg_amop_indices[]; extern char *Name_pg_amproc_indices[]; extern char *Name_pg_attr_indices[]; extern char *Name_pg_attrdef_indices[]; +extern char *Name_pg_cast_indices[]; extern char *Name_pg_class_indices[]; extern char *Name_pg_constraint_indices[]; extern char *Name_pg_conversion_indices[]; @@ -166,6 +169,7 @@ DECLARE_UNIQUE_INDEX(pg_attrdef_adrelid_adnum_index on pg_attrdef using btree(ad DECLARE_UNIQUE_INDEX(pg_attrdef_oid_index on pg_attrdef using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_attribute_relid_attnam_index on pg_attribute using btree(attrelid oid_ops, attname name_ops)); DECLARE_UNIQUE_INDEX(pg_attribute_relid_attnum_index on pg_attribute using btree(attrelid oid_ops, attnum int2_ops)); +DECLARE_UNIQUE_INDEX(pg_cast_source_target_index on pg_cast using btree(castsource oid_ops, casttarget oid_ops)); DECLARE_UNIQUE_INDEX(pg_class_oid_index on pg_class using btree(oid oid_ops)); DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index on pg_class using btree(relname name_ops, relnamespace oid_ops)); /* This following index is not used for a cache and is not unique */ diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h new file mode 100644 index 0000000000..4e042a396e --- /dev/null +++ b/src/include/catalog/pg_cast.h @@ -0,0 +1,225 @@ +/*------------------------------------------------------------------------- + * + * $Header: /cvsroot/pgsql/src/include/catalog/pg_cast.h,v 1.1 2002/07/18 23:11:30 petere Exp $ + * + * Copyright (c) 2002, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ +#ifndef PG_CAST_H +#define PG_CAST_H + +CATALOG(pg_cast) +{ + Oid castsource; + Oid casttarget; + Oid castfunc; /* 0 = binary compatible */ + bool castimplicit; +} FormData_pg_cast; + +typedef FormData_pg_cast *Form_pg_cast; + +#define Natts_pg_cast 4 +#define Anum_pg_cast_castsource 1 +#define Anum_pg_cast_casttarget 2 +#define Anum_pg_cast_castfunc 3 +#define Anum_pg_cast_castimplicit 4 + +/* ---------------- + * initial contents of pg_cast + * ---------------- + */ + +/* + * binary compatible casts + */ +DATA(insert ( 25 1042 0 t )); +DATA(insert ( 25 1043 0 t )); +DATA(insert ( 1042 25 0 t )); +DATA(insert ( 1042 1043 0 t )); +DATA(insert ( 1043 25 0 t )); +DATA(insert ( 1043 1042 0 t )); + +DATA(insert ( 23 24 0 t )); +DATA(insert ( 23 26 0 t )); +DATA(insert ( 23 2202 0 t )); +DATA(insert ( 23 2203 0 t )); +DATA(insert ( 23 2204 0 t )); +DATA(insert ( 23 2205 0 t )); +DATA(insert ( 23 2206 0 t )); +DATA(insert ( 24 23 0 t )); +DATA(insert ( 24 26 0 t )); +DATA(insert ( 24 2202 0 t )); +DATA(insert ( 24 2203 0 t )); +DATA(insert ( 24 2204 0 t )); +DATA(insert ( 24 2205 0 t )); +DATA(insert ( 24 2206 0 t )); +DATA(insert ( 26 23 0 t )); +DATA(insert ( 26 24 0 t )); +DATA(insert ( 26 2202 0 t )); +DATA(insert ( 26 2203 0 t )); +DATA(insert ( 26 2204 0 t )); +DATA(insert ( 26 2205 0 t )); +DATA(insert ( 26 2206 0 t )); +DATA(insert ( 2202 23 0 t )); +DATA(insert ( 2202 24 0 t )); +DATA(insert ( 2202 26 0 t )); +DATA(insert ( 2202 2203 0 t )); +DATA(insert ( 2202 2204 0 t )); +DATA(insert ( 2202 2205 0 t )); +DATA(insert ( 2202 2206 0 t )); +DATA(insert ( 2203 23 0 t )); +DATA(insert ( 2203 24 0 t )); +DATA(insert ( 2203 26 0 t )); +DATA(insert ( 2203 2202 0 t )); +DATA(insert ( 2203 2204 0 t )); +DATA(insert ( 2203 2205 0 t )); +DATA(insert ( 2203 2206 0 t )); +DATA(insert ( 2204 23 0 t )); +DATA(insert ( 2204 24 0 t )); +DATA(insert ( 2204 26 0 t )); +DATA(insert ( 2204 2202 0 t )); +DATA(insert ( 2204 2203 0 t )); +DATA(insert ( 2204 2205 0 t )); +DATA(insert ( 2204 2206 0 t )); +DATA(insert ( 2205 23 0 t )); +DATA(insert ( 2205 24 0 t )); +DATA(insert ( 2205 26 0 t )); +DATA(insert ( 2205 2202 0 t )); +DATA(insert ( 2205 2203 0 t )); +DATA(insert ( 2205 2204 0 t )); +DATA(insert ( 2205 2206 0 t )); +DATA(insert ( 2206 23 0 t )); +DATA(insert ( 2206 24 0 t )); +DATA(insert ( 2206 26 0 t )); +DATA(insert ( 2206 2202 0 t )); +DATA(insert ( 2206 2203 0 t )); +DATA(insert ( 2206 2204 0 t )); +DATA(insert ( 2206 2205 0 t )); + +DATA(insert ( 23 702 0 t )); +DATA(insert ( 702 23 0 t )); + +DATA(insert ( 23 703 0 t )); +DATA(insert ( 703 23 0 t )); + +DATA(insert ( 650 869 0 t )); +DATA(insert ( 869 650 0 t )); + +DATA(insert ( 1560 1562 0 t )); +DATA(insert ( 1562 1560 0 t )); + +/* + * regular casts through a function + * + * This list can be obtained from the following query as long as the + * naming convention of the cast functions remains the same: + * + * select p.proargtypes[0] as source, p.prorettype as target, p.oid as func, p.proimplicit as implicit from pg_proc p, pg_type t where p.pronargs=1 and p.proname = t.typname and p.prorettype = t.oid order by 1, 2; + */ +DATA(insert ( 18 25 946 t )); +DATA(insert ( 18 1042 860 t )); +DATA(insert ( 19 25 406 t )); +DATA(insert ( 19 1042 408 t )); +DATA(insert ( 19 1043 1401 t )); +DATA(insert ( 20 21 714 t )); +DATA(insert ( 20 23 480 t )); +DATA(insert ( 20 25 1288 t )); +DATA(insert ( 20 701 482 t )); +DATA(insert ( 20 1043 1623 f )); +DATA(insert ( 20 1700 1781 t )); +DATA(insert ( 21 20 754 t )); +DATA(insert ( 21 23 313 t )); +DATA(insert ( 21 25 113 t )); +DATA(insert ( 21 700 236 t )); +DATA(insert ( 21 701 235 t )); +DATA(insert ( 21 1700 1782 t )); +DATA(insert ( 23 20 481 t )); +DATA(insert ( 23 21 314 t )); +DATA(insert ( 23 25 112 t )); +DATA(insert ( 23 700 318 t )); +DATA(insert ( 23 701 316 t )); +/*xDATA(insert ( 23 703 1200 f ));*/ +DATA(insert ( 23 1043 1619 f )); +DATA(insert ( 23 1700 1740 t )); +DATA(insert ( 25 18 944 t )); +DATA(insert ( 25 19 407 t )); +DATA(insert ( 25 20 1289 f )); +DATA(insert ( 25 21 818 f )); +DATA(insert ( 25 23 819 f )); +DATA(insert ( 25 26 817 f )); +DATA(insert ( 25 650 1714 f )); +DATA(insert ( 25 700 839 f )); +DATA(insert ( 25 701 838 f )); +DATA(insert ( 25 829 767 f )); +DATA(insert ( 25 869 1713 f )); +DATA(insert ( 25 1082 748 f )); +DATA(insert ( 25 1083 837 f )); +DATA(insert ( 25 1114 2022 f )); +DATA(insert ( 25 1184 1191 f )); +DATA(insert ( 25 1186 1263 f )); +DATA(insert ( 25 1266 938 f )); +DATA(insert ( 26 25 114 f )); +DATA(insert ( 601 600 1532 f )); +DATA(insert ( 602 600 1533 f )); +DATA(insert ( 602 604 1449 f )); +DATA(insert ( 603 600 1534 f )); +DATA(insert ( 603 601 1541 f )); +DATA(insert ( 603 604 1448 f )); +DATA(insert ( 603 718 1479 f )); +DATA(insert ( 604 600 1540 f )); +DATA(insert ( 604 602 1447 f )); +DATA(insert ( 604 603 1446 f )); +DATA(insert ( 604 718 1474 f )); +DATA(insert ( 700 21 238 f )); +DATA(insert ( 700 23 319 f )); +DATA(insert ( 700 25 841 t )); +DATA(insert ( 700 701 311 t )); +DATA(insert ( 700 1700 1742 t )); +DATA(insert ( 701 20 483 f )); +DATA(insert ( 701 21 237 f )); +DATA(insert ( 701 23 317 f )); +DATA(insert ( 701 25 840 t )); +DATA(insert ( 701 700 312 t )); +DATA(insert ( 701 1700 1743 t )); +DATA(insert ( 702 1082 1179 f )); +DATA(insert ( 702 1083 1364 f )); +DATA(insert ( 702 1114 2023 t )); +DATA(insert ( 702 1184 1173 t )); +DATA(insert ( 703 1186 1177 t )); +DATA(insert ( 718 600 1416 f )); +DATA(insert ( 718 603 1480 f )); +DATA(insert ( 718 604 1544 f )); +DATA(insert ( 829 25 752 f )); +DATA(insert ( 869 25 730 f )); +DATA(insert ( 1042 19 409 t )); +DATA(insert ( 1043 19 1400 t )); +DATA(insert ( 1082 25 749 t )); +DATA(insert ( 1082 1114 2024 t )); +DATA(insert ( 1082 1184 1174 t )); +DATA(insert ( 1083 25 948 t )); +DATA(insert ( 1083 1186 1370 t )); +DATA(insert ( 1083 1266 2047 t )); +DATA(insert ( 1114 25 2034 t )); +DATA(insert ( 1114 702 2030 f )); +DATA(insert ( 1114 1082 2029 f )); +DATA(insert ( 1114 1083 1316 f )); +DATA(insert ( 1114 1184 2028 t )); +DATA(insert ( 1184 25 1192 t )); +DATA(insert ( 1184 702 1180 f )); +DATA(insert ( 1184 1082 1178 f )); +DATA(insert ( 1184 1083 2019 f )); +DATA(insert ( 1184 1114 2027 t )); +DATA(insert ( 1184 1266 1388 f )); +DATA(insert ( 1186 25 1193 t )); +DATA(insert ( 1186 703 1194 f )); +DATA(insert ( 1186 1083 1419 f )); +DATA(insert ( 1266 25 939 t )); +DATA(insert ( 1266 1083 2046 t )); +DATA(insert ( 1700 20 1779 f )); +DATA(insert ( 1700 21 1783 f )); +DATA(insert ( 1700 23 1744 f )); +DATA(insert ( 1700 700 1745 f )); +DATA(insert ( 1700 701 1746 f )); + +#endif /* PG_CAST_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 0bb719a55a..c84ac13a95 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.243 2002/06/20 20:29:44 momjian Exp $ + * $Id: pg_proc.h,v 1.244 2002/07/18 23:11:30 petere Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -45,7 +45,7 @@ CATALOG(pg_proc) BOOTSTRAP Oid prolang; /* OID of pg_language entry */ bool proisagg; /* is it an aggregate? */ bool prosecdef; /* security definer */ - bool proimplicit; /* can be invoked as implicit coercion? */ + bool proimplicit; /* unused */ bool proisstrict; /* strict with respect to NULLs? */ bool proretset; /* returns a set? */ char provolatile; /* see PROVOLATILE_ categories below */ @@ -3007,7 +3007,6 @@ extern Oid ProcedureCreate(const char *procedureName, const char *probin, bool isAgg, bool security_definer, - bool isImplicit, bool isStrict, char volatility, int32 byte_pct, diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 169ec3f3df..707ba1d1b8 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: defrem.h,v 1.41 2002/07/12 18:43:19 tgl Exp $ + * $Id: defrem.h,v 1.42 2002/07/18 23:11:32 petere Exp $ * *------------------------------------------------------------------------- */ @@ -42,6 +42,9 @@ extern void ReindexDatabase(const char *databaseName, bool force, bool all); extern void CreateFunction(CreateFunctionStmt *stmt); extern void RemoveFunction(RemoveFuncStmt *stmt); extern void RemoveFunctionById(Oid funcOid); +extern void CreateCast(CreateCastStmt *stmt); +extern void DropCast(DropCastStmt *stmt); +extern void DropCastById(Oid castOid); extern void DefineOperator(List *names, List *parameters); extern void RemoveOperator(RemoveOperStmt *stmt); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 308bf95877..3583315a27 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.112 2002/07/18 17:14:20 momjian Exp $ + * $Id: nodes.h,v 1.113 2002/07/18 23:11:32 petere Exp $ * *------------------------------------------------------------------------- */ @@ -199,6 +199,8 @@ typedef enum NodeTag T_AlterDatabaseSetStmt, T_AlterUserSetStmt, T_CreateConversionStmt, + T_CreateCastStmt, + T_DropCastStmt, T_A_Expr = 700, T_ColumnRef, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index b2e42ab716..e4c6b625d1 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.192 2002/07/18 17:14:20 momjian Exp $ + * $Id: parsenodes.h,v 1.193 2002/07/18 23:11:32 petere Exp $ * *------------------------------------------------------------------------- */ @@ -1555,4 +1555,30 @@ typedef struct CreateConversionStmt bool def; /* is this a default conversion? */ } CreateConversionStmt; +/* ---------------------- + * CREATE CAST Statement + * ---------------------- + */ +typedef struct CreateCastStmt +{ + NodeTag type; + TypeName *sourcetype; + TypeName *targettype; + FuncWithArgs *func; + bool implicit; +} CreateCastStmt; + +/* ---------------------- + * DROP CAST Statement + * ---------------------- + */ +typedef struct DropCastStmt +{ + NodeTag type; + TypeName *sourcetype; + TypeName *targettype; + DropBehavior behavior; +} DropCastStmt; + + #endif /* PARSENODES_H */ diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index f40471d794..5d964bb566 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: syscache.h,v 1.49 2002/07/11 07:39:28 ishii Exp $ + * $Id: syscache.h,v 1.50 2002/07/18 23:11:32 petere Exp $ * *------------------------------------------------------------------------- */ @@ -36,29 +36,30 @@ #define AMPROCNUM 5 #define ATTNAME 6 #define ATTNUM 7 -#define CLAAMNAMENSP 8 -#define CLAOID 9 -#define CONNAMESP 10 -#define GRONAME 11 -#define GROSYSID 12 -#define INDEXRELID 13 -#define INHRELID 14 -#define LANGNAME 15 -#define LANGOID 16 -#define NAMESPACENAME 17 -#define NAMESPACEOID 18 -#define OPERNAMENSP 19 -#define OPEROID 20 -#define PROCNAMENSP 21 -#define PROCOID 22 -#define RELNAMENSP 23 -#define RELOID 24 -#define RULERELNAME 25 -#define SHADOWNAME 26 -#define SHADOWSYSID 27 -#define STATRELATT 28 -#define TYPENAMENSP 29 -#define TYPEOID 30 +#define CASTSOURCETARGET 8 +#define CLAAMNAMENSP 9 +#define CLAOID 10 +#define CONNAMESP 11 +#define GRONAME 12 +#define GROSYSID 13 +#define INDEXRELID 14 +#define INHRELID 15 +#define LANGNAME 16 +#define LANGOID 17 +#define NAMESPACENAME 18 +#define NAMESPACEOID 19 +#define OPERNAMENSP 20 +#define OPEROID 21 +#define PROCNAMENSP 22 +#define PROCOID 23 +#define RELNAMENSP 24 +#define RELOID 25 +#define RULERELNAME 26 +#define SHADOWNAME 27 +#define SHADOWSYSID 28 +#define STATRELATT 29 +#define TYPENAMENSP 30 +#define TYPEOID 31 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 98ac26c0c2..cb1193b040 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1,7 +1,7 @@ -- -- OPR_SANITY -- Sanity checks for common errors in making operator/procedure system tables: --- pg_operator, pg_proc, pg_aggregate, pg_am, pg_amop, pg_amproc, pg_opclass. +-- pg_operator, pg_proc, pg_cast, pg_aggregate, pg_am, pg_amop, pg_amproc, pg_opclass. -- -- None of the SELECTs here should ever find any matching entries, -- so the expected output is easy to maintain ;-). @@ -180,20 +180,49 @@ WHERE p1.oid != p2.oid AND -------------+------------- (0 rows) --- If a proc is marked as an implicit cast, then it should be something that --- the system might actually use as a cast function: name same as the name --- of its output type, and either one arg that's a different type, or two --- args where the first is the same as the output type and the second is int4. -SELECT p1.oid, p1.proname -FROM pg_proc as p1 -WHERE p1.proimplicit AND - (NOT EXISTS (SELECT 1 FROM pg_type t WHERE t.oid = p1.prorettype AND - t.typname = p1.proname) OR - NOT ((p1.pronargs = 1 AND p1.proargtypes[0] != prorettype) OR - (p1.pronargs = 2 AND p1.proargtypes[0] = prorettype AND - p1.proargtypes[1] = 'int4'::regtype))); - oid | proname ------+--------- +-- **************** pg_cast **************** +-- Look for casts from and to the same type. This is not harmful, but +-- useless. +SELECT * +FROM pg_cast c +WHERE c.castsource = c.casttarget; + castsource | casttarget | castfunc | castimplicit +------------+------------+----------+-------------- +(0 rows) + +-- Look for cast functions with incorrect number or type of argument +-- or return value. +SELECT c.* +FROM pg_cast c, pg_proc p +WHERE c.castfunc = p.oid AND + (p.pronargs <> 1 OR + p.proargtypes[0] <> c.castsource OR + p.prorettype <> c.casttarget); + castsource | casttarget | castfunc | castimplicit +------------+------------+----------+-------------- +(0 rows) + +-- Look for binary compatible casts that are not implicit. This is +-- legal, but probably not intended. +SELECT * +FROM pg_cast c +WHERE c.castfunc = 0 AND NOT c.castimplicit; + castsource | casttarget | castfunc | castimplicit +------------+------------+----------+-------------- +(0 rows) + +-- Look for binary compatible casts that do not have the reverse +-- direction registered as well, or where the reverse direction is not +-- also binary compatible. This is legal, but probably not intended. +SELECT * +FROM pg_cast c +WHERE c.castfunc = 0 AND + NOT EXISTS (SELECT * FROM pg_cast k + WHERE k.castfunc = 0 AND + k.castsource = c.casttarget AND + k.casttarget = c.castsource); + castsource | casttarget | castfunc | castimplicit +------------+------------+----------+-------------- (0 rows) -- **************** pg_operator **************** diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 2e0cf3a033..25e7b091c2 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -37,6 +37,7 @@ SELECT relname, relhasindex pg_amproc | t pg_attrdef | t pg_attribute | t + pg_cast | t pg_class | t pg_constraint | t pg_conversion | t @@ -62,5 +63,5 @@ SELECT relname, relhasindex shighway | t tenk1 | t tenk2 | t -(52 rows) +(53 rows) diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 270c275b87..9d08dbd5bb 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -1,7 +1,7 @@ -- -- OPR_SANITY -- Sanity checks for common errors in making operator/procedure system tables: --- pg_operator, pg_proc, pg_aggregate, pg_am, pg_amop, pg_amproc, pg_opclass. +-- pg_operator, pg_proc, pg_cast, pg_aggregate, pg_am, pg_amop, pg_amproc, pg_opclass. -- -- None of the SELECTs here should ever find any matching entries, -- so the expected output is easy to maintain ;-). @@ -141,19 +141,43 @@ WHERE p1.oid != p2.oid AND NOT p1.proisagg AND NOT p2.proisagg AND (p1.proargtypes[7] < p2.proargtypes[7]); --- If a proc is marked as an implicit cast, then it should be something that --- the system might actually use as a cast function: name same as the name --- of its output type, and either one arg that's a different type, or two --- args where the first is the same as the output type and the second is int4. +-- **************** pg_cast **************** -SELECT p1.oid, p1.proname -FROM pg_proc as p1 -WHERE p1.proimplicit AND - (NOT EXISTS (SELECT 1 FROM pg_type t WHERE t.oid = p1.prorettype AND - t.typname = p1.proname) OR - NOT ((p1.pronargs = 1 AND p1.proargtypes[0] != prorettype) OR - (p1.pronargs = 2 AND p1.proargtypes[0] = prorettype AND - p1.proargtypes[1] = 'int4'::regtype))); +-- Look for casts from and to the same type. This is not harmful, but +-- useless. + +SELECT * +FROM pg_cast c +WHERE c.castsource = c.casttarget; + +-- Look for cast functions with incorrect number or type of argument +-- or return value. + +SELECT c.* +FROM pg_cast c, pg_proc p +WHERE c.castfunc = p.oid AND + (p.pronargs <> 1 OR + p.proargtypes[0] <> c.castsource OR + p.prorettype <> c.casttarget); + +-- Look for binary compatible casts that are not implicit. This is +-- legal, but probably not intended. + +SELECT * +FROM pg_cast c +WHERE c.castfunc = 0 AND NOT c.castimplicit; + +-- Look for binary compatible casts that do not have the reverse +-- direction registered as well, or where the reverse direction is not +-- also binary compatible. This is legal, but probably not intended. + +SELECT * +FROM pg_cast c +WHERE c.castfunc = 0 AND + NOT EXISTS (SELECT * FROM pg_cast k + WHERE k.castfunc = 0 AND + k.castsource = c.casttarget AND + k.casttarget = c.castsource); -- **************** pg_operator **************** -- 2.40.0