From: Tom Lane Date: Mon, 21 Nov 2011 04:50:27 +0000 (-0500) Subject: Further code review for range types patch. X-Git-Tag: REL9_2_BETA1~803 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b985d48779146b7ba969b0963614ad7683589bc8;p=postgresql Further code review for range types patch. Fix some bugs in coercion logic and pg_dump; more comment cleanup; minor cosmetic improvements. --- diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index fe59a1c776..152ef2f037 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4506,6 +4506,12 @@ SELECT * FROM pg_attribute Indicates that a function accepts any input data type. + + anyelement + Indicates that a function accepts any data type + (see ). + + anyarray Indicates that a function accepts any array data type @@ -4513,8 +4519,8 @@ SELECT * FROM pg_attribute - anyelement - Indicates that a function accepts any data type + anynonarray + Indicates that a function accepts any non-array data type (see ). @@ -4532,12 +4538,6 @@ SELECT * FROM pg_attribute ). - - anynonarray - Indicates that a function accepts any non-array data type - (see ). - - cstring Indicates that a function accepts or returns a null-terminated C string. @@ -4595,9 +4595,9 @@ SELECT * FROM pg_attribute languages all forbid use of a pseudo-type as argument type, and allow only void and record as a result type (plus trigger when the function is used as a trigger). Some also - support polymorphic functions using the types anyarray, - anyelement, anyenum, anyrange, and - anynonarray. + support polymorphic functions using the types anyelement, + anyarray, anynonarray, anyenum, and + anyrange. diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 8378c360b9..a70a2aaece 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -91,7 +91,7 @@ ProcedureCreate(const char *procedureName, int parameterCount; int allParamCount; Oid *allParams; - char *modes = NULL; + char *paramModes = NULL; bool genericInParam = false; bool genericOutParam = false; bool anyrangeInParam = false; @@ -130,6 +130,7 @@ ProcedureCreate(const char *procedureName, FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ + /* Deconstruct array inputs */ if (allParameterTypes != PointerGetDatum(NULL)) { /* @@ -169,28 +170,27 @@ ProcedureCreate(const char *procedureName, ARR_HASNULL(modesArray) || ARR_ELEMTYPE(modesArray) != CHAROID) elog(ERROR, "parameterModes is not a 1-D char array"); - modes = (char *) ARR_DATA_PTR(modesArray); + paramModes = (char *) ARR_DATA_PTR(modesArray); } - /* - * Do not allow polymorphic return type unless at least one input argument - * is polymorphic. Also, do not allow return type INTERNAL unless at - * least one input argument is INTERNAL. + * Detect whether we have polymorphic or INTERNAL arguments. The first + * loop checks input arguments, the second output arguments. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { - case ANYRANGEOID: - anyrangeInParam = true; - /* FALL THROUGH */ case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; + case ANYRANGEOID: + genericInParam = true; + anyrangeInParam = true; + break; case INTERNALOID: internalInParam = true; break; @@ -201,23 +201,23 @@ ProcedureCreate(const char *procedureName, { for (i = 0; i < allParamCount; i++) { - if (modes == NULL || - (modes[i] != PROARGMODE_OUT && - modes[i] != PROARGMODE_INOUT && - modes[i] != PROARGMODE_TABLE)) - continue; + if (paramModes == NULL || + paramModes[i] == PROARGMODE_IN || + paramModes[i] == PROARGMODE_VARIADIC) + continue; /* ignore input-only params */ switch (allParams[i]) { - case ANYRANGEOID: - anyrangeOutParam = true; - /* FALL THROUGH */ case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; + case ANYRANGEOID: + genericOutParam = true; + anyrangeOutParam = true; + break; case INTERNALOID: internalOutParam = true; break; @@ -225,6 +225,13 @@ ProcedureCreate(const char *procedureName, } } + /* + * Do not allow polymorphic return type unless at least one input argument + * is polymorphic. ANYRANGE return type is even stricter: must have an + * ANYRANGE input (since we can't deduce the specific range type from + * ANYELEMENT). Also, do not allow return type INTERNAL unless at least + * one input argument is INTERNAL. + */ if ((IsPolymorphicType(returnType) || genericOutParam) && !genericInParam) ereport(ERROR, @@ -259,7 +266,7 @@ ProcedureCreate(const char *procedureName, procedureName, format_type_be(parameterTypes->values[0])))); - if (modes != NULL) + if (paramModes != NULL) { /* * Only the last input parameter can be variadic; if it is, save its @@ -268,7 +275,7 @@ ProcedureCreate(const char *procedureName, */ for (i = 0; i < allParamCount; i++) { - switch (modes[i]) + switch (paramModes[i]) { case PROARGMODE_IN: case PROARGMODE_INOUT: @@ -298,7 +305,7 @@ ProcedureCreate(const char *procedureName, } break; default: - elog(ERROR, "invalid parameter mode '%c'", modes[i]); + elog(ERROR, "invalid parameter mode '%c'", paramModes[i]); break; } } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index a1628bc098..8ffbc52fde 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -654,9 +654,9 @@ RemoveTypeById(Oid typeOid) EnumValuesDelete(typeOid); /* - * If it is a range type, delete the pg_range entries too; we don't bother - * with making dependency entries for those, so it has to be done "by - * hand" here. + * If it is a range type, delete the pg_range entry too; we don't bother + * with making a dependency entry for that, so it has to be done "by hand" + * here. */ if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE) RangeDelete(typeOid); @@ -1475,8 +1475,9 @@ makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype) 0.0); /* prorows */ /* - * Make the constructor internally-dependent on the range type so that - * the user doesn't have to treat them as separate objects. + * Make the constructors internally-dependent on the range type so + * that they go away silently when the type is dropped. Note that + * pg_dump depends on this choice to avoid dumping the constructors. */ myself.classId = ProcedureRelationId; myself.objectId = procOid; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index a461ac9aef..c07232544a 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -165,11 +165,12 @@ coerce_type(ParseState *pstate, Node *node, * Assume can_coerce_type verified that implicit coercion is okay. * * These cases are unlike the ones above because the exposed type of - * the argument must be an actual array or enum type. In particular - * the argument must *not* be an UNKNOWN constant. If it is, we just - * fall through; below, we'll call anyarray_in or anyenum_in, which - * will produce an error. Also, if what we have is a domain over - * array or enum, we have to relabel it to its base type. + * the argument must be an actual array, enum, or range type. In + * particular the argument must *not* be an UNKNOWN constant. If it + * is, we just fall through; below, we'll call anyarray_in, + * anyenum_in, or anyrange_in, which will produce an error. Also, if + * what we have is a domain over array, enum, or range, we have to + * relabel it to its base type. * * Note: currently, we can't actually see a domain-over-enum here, * since the other functions in this file will not match such a @@ -1273,27 +1274,36 @@ coerce_to_common_type(ParseState *pstate, Node *node, * * The argument consistency rules are: * - * 1) All arguments declared ANYARRAY must have matching datatypes, - * and must in fact be varlena arrays. - * 2) All arguments declared ANYELEMENT must have matching datatypes. - * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the - * actual ANYELEMENT datatype is in fact the element type for the actual - * ANYARRAY datatype. Similarly, if there are arguments of both ANYELEMENT - * and ANYRANGE, make sure the actual ANYELEMENT datatype is in fact the - * subtype for the actual ANYRANGE type. - * 4) ANYENUM is treated the same as ANYELEMENT except that if it is used - * (alone or in combination with plain ANYELEMENT), we add the extra - * condition that the ANYELEMENT type must be an enum. - * 5) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, - * we add the extra condition that the ANYELEMENT type must not be an array. - * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but - * is an extra restriction if not.) + * 1) All arguments declared ANYELEMENT must have the same datatype. + * 2) All arguments declared ANYARRAY must have the same datatype, + * which must be a varlena array type. + * 3) All arguments declared ANYRANGE must have the same datatype, + * which must be a range type. + * 4) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the + * actual ANYELEMENT datatype is in fact the element type for the actual + * ANYARRAY datatype. + * 5) Similarly, if there are arguments of both ANYELEMENT and ANYRANGE, + * make sure the actual ANYELEMENT datatype is in fact the subtype for + * the actual ANYRANGE type. + * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used + * (alone or in combination with plain ANYELEMENT), we add the extra + * condition that the ANYELEMENT type must be an enum. + * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, + * we add the extra condition that the ANYELEMENT type must not be an array. + * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but + * is an extra restriction if not.) * * Domains over arrays match ANYARRAY, and are immediately flattened to their * base type. (Thus, for example, we will consider it a match if one ANYARRAY - * argument is a domain over int4[] while another one is just int4[].) Also + * argument is a domain over int4[] while another one is just int4[].) Also * notice that such a domain does *not* match ANYNONARRAY. * + * Similarly, domains over ranges match ANYRANGE, and are immediately + * flattened to their base type. + * + * Note that domains aren't currently considered to match ANYENUM, + * even if their base type would match. + * * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic * argument, assume it is okay. * @@ -1357,7 +1367,7 @@ check_generic_type_consistency(Oid *actual_arg_types, { if (actual_type == UNKNOWNOID) continue; - actual_type = getBaseType(actual_type); + actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(range_typeid) && actual_type != range_typeid) return false; range_typeid = actual_type; @@ -1393,20 +1403,6 @@ check_generic_type_consistency(Oid *actual_arg_types, } } - if (have_anynonarray) - { - /* require the element type to not be an array or domain over array */ - if (type_is_array_domain(elem_typeid)) - return false; - } - - if (have_anyenum) - { - /* require the element type to be an enum */ - if (!type_is_enum(elem_typeid)) - return false; - } - /* Get the element type based on the range type, if we have one */ if (OidIsValid(range_typeid)) { @@ -1428,6 +1424,20 @@ check_generic_type_consistency(Oid *actual_arg_types, } } + if (have_anynonarray) + { + /* require the element type to not be an array or domain over array */ + if (type_is_array_domain(elem_typeid)) + return false; + } + + if (have_anyenum) + { + /* require the element type to be an enum */ + if (!type_is_enum(elem_typeid)) + return false; + } + /* Looks valid */ return true; } @@ -1439,7 +1449,7 @@ check_generic_type_consistency(Oid *actual_arg_types, * * If any polymorphic pseudotype is used in a function's arguments or * return type, we make sure the actual data types are consistent with - * each other. The argument consistency rules are shown above for + * each other. The argument consistency rules are shown above for * check_generic_type_consistency(). * * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic @@ -1451,51 +1461,51 @@ check_generic_type_consistency(Oid *actual_arg_types, * if it is declared as a polymorphic type: * * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the - * argument's actual type as the function's return type. Similarly, if - * return type is ANYRANGE, and any argument is ANYRANGE, use the argument's - * actual type as the function's return type. - * 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is - * ANYELEMENT, use the actual type of the argument to determine the - * function's return type, i.e. the element type's corresponding array - * type. Note: similar behavior does not exist for ANYRANGE, because it's - * impossble to determine the range type from the subtype alone.\ - * 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT, - * generate an ERROR. Similarly, if the return type is ANYRANGE, and no - * argument is ANYRANGE or ANYELEMENT, generate an error. These conditions - * are prevented by CREATE FUNCTION and is therefore not expected here. - * 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the - * argument's actual type as the function's return type. - * 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument - * is ANYARRAY or ANYRANGE, use the actual type of the argument to determine - * the function's return type; i.e. the array type's corresponding element - * type or the range type's corresponding subtype (or both, in which case - * they must match). - * 6) If return type is ANYELEMENT, no argument is ANYARRAY, ANYRANGE, or - * ANYELEMENT, generate an ERROR. This condition is prevented by CREATE - * FUNCTION and is therefore not expected here. - * 7) ANYENUM is treated the same as ANYELEMENT except that if it is used - * (alone or in combination with plain ANYELEMENT), we add the extra - * condition that the ANYELEMENT type must be an enum. - * 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, - * we add the extra condition that the ANYELEMENT type must not be an array. - * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but - * is an extra restriction if not.) + * argument's actual type as the function's return type. + * 2) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE, + * use the argument's actual type as the function's return type. + * 3) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is + * ANYELEMENT, use the actual type of the argument to determine the + * function's return type, i.e. the element type's corresponding array + * type. (Note: similar behavior does not exist for ANYRANGE, because it's + * impossible to determine the range type from the subtype alone.) + * 4) If return type is ANYARRAY, but no argument is ANYARRAY or ANYELEMENT, + * generate an error. Similarly, if return type is ANYRANGE, but no + * argument is ANYRANGE, generate an error. (These conditions are + * prevented by CREATE FUNCTION and therefore are not expected here.) + * 5) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the + * argument's actual type as the function's return type. + * 6) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument + * is ANYARRAY or ANYRANGE, use the actual type of the argument to determine + * the function's return type, i.e. the array type's corresponding element + * type or the range type's corresponding subtype (or both, in which case + * they must match). + * 7) If return type is ANYELEMENT, no argument is ANYELEMENT, ANYARRAY, or + * ANYRANGE, generate an error. (This condition is prevented by CREATE + * FUNCTION and therefore is not expected here.) + * 8) ANYENUM is treated the same as ANYELEMENT except that if it is used + * (alone or in combination with plain ANYELEMENT), we add the extra + * condition that the ANYELEMENT type must be an enum. + * 9) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used, + * we add the extra condition that the ANYELEMENT type must not be an array. + * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but + * is an extra restriction if not.) * * Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments, * respectively, and are immediately flattened to their base type. (In - * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set it to - * the base type not the domain type.) + * particular, if the return type is also ANYARRAY or ANYRANGE, we'll set it + * to the base type not the domain type.) * * When allow_poly is false, we are not expecting any of the actual_arg_types * to be polymorphic, and we should not return a polymorphic result type - * either. When allow_poly is true, it is okay to have polymorphic "actual" - * arg types, and we can return ANYARRAY or ANYELEMENT as the result. (This - * case is currently used only to check compatibility of an aggregate's - * declaration with the underlying transfn.) + * either. When allow_poly is true, it is okay to have polymorphic "actual" + * arg types, and we can return ANYARRAY, ANYRANGE, or ANYELEMENT as the + * result. (This case is currently used only to check compatibility of an + * aggregate's declaration with the underlying transfn.) * * A special case is that we could see ANYARRAY as an actual_arg_type even * when allow_poly is false (this is possible only because pg_statistic has - * columns shown as anyarray in the catalogs). We allow this to match a + * columns shown as anyarray in the catalogs). We allow this to match a * declared ANYARRAY argument, but only if there is no ANYELEMENT argument * or result (since we can't determine a specific element type to match to * ANYELEMENT). Note this means that functions taking ANYARRAY had better @@ -1612,7 +1622,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, if (array_typeid == ANYARRAYOID && !have_anyelement) { /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ - array_typelem = InvalidOid; + array_typelem = ANYELEMENTOID; } else { @@ -1642,15 +1652,24 @@ enforce_generic_type_consistency(Oid *actual_arg_types, format_type_be(elem_typeid)))); } } + /* Get the element type based on the range type, if we have one */ - else if (OidIsValid(range_typeid)) + if (OidIsValid(range_typeid)) { - range_typelem = get_range_subtype(range_typeid); - if (!OidIsValid(range_typelem)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyrange\" is not a range but type %s", - format_type_be(range_typeid)))); + if (range_typeid == ANYRANGEOID && !have_anyelement) + { + /* Special case for ANYRANGE input: okay iff no ANYELEMENT */ + range_typelem = ANYELEMENTOID; + } + else + { + range_typelem = get_range_subtype(range_typeid); + if (!OidIsValid(range_typelem)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("argument declared \"anyrange\" is not a range but type %s", + format_type_be(range_typeid)))); + } if (!OidIsValid(elem_typeid)) { @@ -1670,12 +1689,14 @@ enforce_generic_type_consistency(Oid *actual_arg_types, format_type_be(elem_typeid)))); } } - else if (!OidIsValid(elem_typeid)) + + if (!OidIsValid(elem_typeid)) { if (allow_poly) { - array_typeid = ANYARRAYOID; elem_typeid = ANYELEMENTOID; + array_typeid = ANYARRAYOID; + range_typeid = ANYRANGEOID; } else { @@ -1861,13 +1882,14 @@ resolve_generic_type(Oid declared_type, else if (context_declared_type == ANYRANGEOID) { /* Use the element type corresponding to actual type */ - Oid range_typelem = get_range_subtype(context_actual_type); + Oid context_base_type = getBaseType(context_actual_type); + Oid range_typelem = get_range_subtype(context_base_type); if (!OidIsValid(range_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyrange\" is not a range but type %s", - format_type_be(context_actual_type)))); + format_type_be(context_base_type)))); return range_typelem; } else if (context_declared_type == ANYELEMENTOID || @@ -1970,12 +1992,12 @@ IsBinaryCoercible(Oid srctype, Oid targettype) /* Also accept any array type as coercible to ANYARRAY */ if (targettype == ANYARRAYOID) - if (type_is_array_domain(srctype)) + if (type_is_array(srctype)) return true; /* Also accept any non-array type as coercible to ANYNONARRAY */ if (targettype == ANYNONARRAYOID) - if (!type_is_array_domain(srctype)) + if (!type_is_array(srctype)) return true; /* Also accept any enum type as coercible to ANYENUM */ diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index cc8f32d4ce..6f88c476ad 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -851,11 +851,11 @@ standard_ProcessUtility(Node *parsetree, } break; - case T_CreateEnumStmt: /* CREATE TYPE (enum) */ + case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */ DefineEnum((CreateEnumStmt *) parsetree); break; - case T_CreateRangeStmt: + case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */ DefineRange((CreateRangeStmt *) parsetree); break; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 1b4d26d659..ceca0e3ede 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -2253,7 +2253,7 @@ type_is_enum(Oid typid) /* * type_is_range - * Returns true if the given type is an range type. + * Returns true if the given type is a range type. */ bool type_is_range(Oid typid) @@ -2867,6 +2867,14 @@ get_namespace_name(Oid nspid) return NULL; } +/* ---------- PG_RANGE CACHE ---------- */ + +/* + * get_range_subtype + * Returns the subtype of a given range type + * + * Returns InvalidOid if the type is not a range type. + */ Oid get_range_subtype(Oid rangeOid) { diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 71b09abb23..043030bd70 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -555,7 +555,7 @@ static const struct cachedesc cacheinfo[] = { }, 2048 }, - {RangeRelationId, /* RANGETYPE */ + {RangeRelationId, /* RANGETYPE */ RangeTypidIndexId, 1, { @@ -564,7 +564,7 @@ static const struct cachedesc cacheinfo[] = { 0, 0 }, - 1024 + 64 }, {RelationRelationId, /* RELNAMENSP */ ClassNameNspIndexId, diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 3cc2a7ee07..d19be87085 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -481,14 +481,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, !OidIsValid(anyrange_type)) return false; - /* - * We can't deduce a range type from the subtype, because there may be - * multiple range types for a single subtype. - */ - if (have_anyrange_result && !OidIsValid(anyrange_type)) - return false; - - /* If needed, deduce one polymorphic type from the other */ + /* If needed, deduce one polymorphic type from others */ if (have_anyelement_result && !OidIsValid(anyelement_type)) { if (OidIsValid(anyarray_type)) @@ -497,14 +490,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, ANYARRAYOID); if (OidIsValid(anyrange_type)) { - Oid subtype = resolve_generic_type(ANYELEMENTOID, - anyrange_type, - ANYRANGEOID); - if (OidIsValid(anyelement_type) && - anyelement_type != subtype) + Oid subtype = resolve_generic_type(ANYELEMENTOID, + anyrange_type, + ANYRANGEOID); + + /* check for inconsistent array and range results */ + if (OidIsValid(anyelement_type) && anyelement_type != subtype) return false; - else - anyelement_type = subtype; + anyelement_type = subtype; } } @@ -513,6 +506,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, anyelement_type, ANYELEMENTOID); + /* + * We can't deduce a range type from other polymorphic inputs, because + * there may be multiple range types for the same subtype. + */ + if (have_anyrange_result && !OidIsValid(anyrange_type)) + return false; + /* Enforce ANYNONARRAY if needed */ if (have_anynonarray && type_is_array(anyelement_type)) return false; @@ -523,9 +523,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, /* * Identify the collation to use for polymorphic OUT parameters. (It'll - * necessarily be the same for both anyelement and anyarray.) + * necessarily be the same for both anyelement and anyarray.) Note that + * range types are not collatable, so any possible internal collation of + * a range type is not considered here. */ - if (OidIsValid(anyelement_type)) anycollation = get_typcollation(anyelement_type); else if (OidIsValid(anyarray_type)) @@ -573,7 +574,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, anyrange_type, -1, 0); - TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); + /* no collation should be attached to a range type */ break; default: break; @@ -672,19 +673,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, !have_anyrange_result) return true; - /* - * We can't deduce a range type from the subtype, because there may be - * multiple range types for a single subtype. - */ - if (have_anyrange_result && !OidIsValid(anyrange_type)) - return false; - /* If no input polymorphics, parser messed up */ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) && !OidIsValid(anyrange_type)) return false; - /* If needed, deduce one polymorphic type from the other */ + /* If needed, deduce one polymorphic type from others */ if (have_anyelement_result && !OidIsValid(anyelement_type)) { if (OidIsValid(anyarray_type)) @@ -693,14 +687,14 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, ANYARRAYOID); if (OidIsValid(anyrange_type)) { - Oid subtype = resolve_generic_type(ANYELEMENTOID, - anyrange_type, - ANYRANGEOID); - if (OidIsValid(anyelement_type) && - anyelement_type != subtype) + Oid subtype = resolve_generic_type(ANYELEMENTOID, + anyrange_type, + ANYRANGEOID); + + /* check for inconsistent array and range results */ + if (OidIsValid(anyelement_type) && anyelement_type != subtype) return false; - else - anyelement_type = subtype; + anyelement_type = subtype; } } @@ -709,6 +703,13 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, anyelement_type, ANYELEMENTOID); + /* + * We can't deduce a range type from other polymorphic inputs, because + * there may be multiple range types for the same subtype. + */ + if (have_anyrange_result && !OidIsValid(anyrange_type)) + return false; + /* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */ /* And finally replace the output column types as needed */ diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index d2005c1e80..41114ac4da 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -52,7 +52,6 @@ #include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject_metadata.h" #include "catalog/pg_proc.h" -#include "catalog/pg_range.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" #include "libpq/libpq-fs.h" @@ -3703,34 +3702,16 @@ getFuncs(int *numFuncs) * pg_catalog. In normal dumps we can still ignore those --- but in * binary-upgrade mode, we must dump the member objects of the extension, * so be sure to fetch any such functions. + * + * Also, in 9.2 and up, exclude functions that are internally dependent on + * something else, since presumably those will be created as a result of + * creating the something else. This currently only acts to suppress + * constructor functions for range types. Note that this is OK only + * because the constructors don't have any dependencies the range type + * doesn't have; otherwise we might not get creation ordering correct. */ - if (g_fout->remoteVersion >= 90200) - { - appendPQExpBuffer(query, - "SELECT tableoid, oid, proname, prolang, " - "pronargs, proargtypes, prorettype, proacl, " - "pronamespace, " - "(%s proowner) AS rolname " - "FROM pg_proc p " - "WHERE NOT proisagg AND " - " NOT EXISTS (SELECT 1 FROM pg_depend " - " WHERE classid = 'pg_proc'::regclass AND " - " objid = p.oid AND deptype = 'i') AND " - "(pronamespace != " - "(SELECT oid FROM pg_namespace " - "WHERE nspname = 'pg_catalog')", - username_subquery); - if (binary_upgrade && g_fout->remoteVersion >= 90100) - appendPQExpBuffer(query, - " OR EXISTS(SELECT 1 FROM pg_depend WHERE " - "classid = 'pg_proc'::regclass AND " - "objid = p.oid AND " - "refclassid = 'pg_extension'::regclass AND " - "deptype = 'e')"); - appendPQExpBuffer(query, ")"); - } - else if (g_fout->remoteVersion >= 70300) + if (g_fout->remoteVersion >= 70300) { appendPQExpBuffer(query, "SELECT tableoid, oid, proname, prolang, " @@ -3743,9 +3724,14 @@ getFuncs(int *numFuncs) "(SELECT oid FROM pg_namespace " "WHERE nspname = 'pg_catalog')", username_subquery); + if (g_fout->remoteVersion >= 90200) + appendPQExpBuffer(query, + "\n AND NOT EXISTS (SELECT 1 FROM pg_depend " + "WHERE classid = 'pg_proc'::regclass AND " + "objid = p.oid AND deptype = 'i')"); if (binary_upgrade && g_fout->remoteVersion >= 90100) appendPQExpBuffer(query, - " OR EXISTS(SELECT 1 FROM pg_depend WHERE " + "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE " "classid = 'pg_proc'::regclass AND " "objid = p.oid AND " "refclassid = 'pg_extension'::regclass AND " @@ -7479,31 +7465,25 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) PQExpBuffer labelq = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer(); PGresult *res; + Oid collationOid; + char *procname; - Oid collationOid; - Oid opclassOid; - Oid analyzeOid; - Oid canonicalOid; - Oid subdiffOid; - - /* Set proper schema search path */ - selectSourceSchema("pg_catalog"); + /* + * select appropriate schema to ensure names in CREATE are properly + * qualified + */ + selectSourceSchema(tyinfo->dobj.namespace->dobj.name); appendPQExpBuffer(query, - "SELECT rngtypid, " - "format_type(rngsubtype, NULL) as rngsubtype, " - "rngsubtype::oid as rngsubtypeoid, " + "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, " "opc.opcname AS opcname, " + "(SELECT nspname FROM pg_catalog.pg_namespace nsp " + " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, " + "opc.opcdefault, " "CASE WHEN rngcollation = st.typcollation THEN 0 " " ELSE rngcollation END AS collation, " - "CASE WHEN opcdefault THEN 0 ELSE rngsubopc END " - " AS rngsubopc, " - "(SELECT nspname FROM pg_namespace nsp " - " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, " - "t.typanalyze, t.typanalyze::oid as typanalyzeoid, " - "rngcanonical, rngcanonical::oid as rngcanonicaloid, " - "rngsubdiff, rngsubdiff::oid as rngsubdiffoid " - "FROM pg_catalog.pg_type t, pg_type st, " + "rngcanonical, rngsubdiff, t.typanalyze " + "FROM pg_catalog.pg_type t, pg_catalog.pg_type st, " " pg_catalog.pg_opclass opc, pg_catalog.pg_range r " "WHERE t.oid = rngtypid AND st.oid = rngsubtype AND " " opc.oid = rngsubopc AND rngtypid = '%u'", @@ -7511,6 +7491,12 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) res = PQexec(g_conn, query->data); check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + if (PQntuples(res) != 1) + { + write_msg(NULL, "query returned %d pg_range entries for range type \"%s\"\n", + PQntuples(res), tyinfo->dobj.name); + exit_nicely(); + } /* * DROP must be fully qualified in case same name appears in pg_catalog. @@ -7522,68 +7508,53 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "%s;\n", fmtId(tyinfo->dobj.name)); - /* We might already have a shell type, but setting pg_type_oid is harmless */ if (binary_upgrade) binary_upgrade_set_type_oids_by_type_oid(q, tyinfo->dobj.catId.oid); appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (", fmtId(tyinfo->dobj.name)); - /* SUBTYPE */ - appendPQExpBuffer(q, "\n SUBTYPE = %s", + appendPQExpBuffer(q, "\n subtype = %s", PQgetvalue(res, 0, PQfnumber(res, "rngsubtype"))); - /* COLLATION */ - collationOid = atooid(PQgetvalue(res, 0, - PQfnumber(res, "collation"))); + /* print subtype_opclass only if not default for subtype */ + if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't') + { + char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname")); + char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp")); + + /* always schema-qualify, don't try to be smart */ + appendPQExpBuffer(q, ",\n subtype_opclass = %s.", + fmtId(nspname)); + appendPQExpBuffer(q, "%s", fmtId(opcname)); + } + + collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation"))); if (OidIsValid(collationOid)) { - CollInfo *coll; + CollInfo *coll = findCollationByOid(collationOid); - coll = findCollationByOid(collationOid); if (coll) { /* always schema-qualify, don't try to be smart */ - appendPQExpBuffer(q, ",\n COLLATION = %s.", + appendPQExpBuffer(q, ",\n collation = %s.", fmtId(coll->dobj.namespace->dobj.name)); appendPQExpBuffer(q, "%s", fmtId(coll->dobj.name)); } } - /* SUBTYPE_OPCLASS */ - opclassOid = atooid(PQgetvalue(res, 0, - PQfnumber(res, "rngsubopc"))); - if (OidIsValid(opclassOid)) - { - char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname")); - char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp")); + procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical")); + if (strcmp(procname, "-") != 0) + appendPQExpBuffer(q, ",\n canonical = %s", procname); - /* always schema-qualify, don't try to be smart */ - appendPQExpBuffer(q, ",\n SUBTYPE_OPCLASS = %s.", - fmtId(nspname)); - appendPQExpBuffer(q, "%s", fmtId(opcname)); - } + procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff")); + if (strcmp(procname, "-") != 0) + appendPQExpBuffer(q, ",\n subtype_diff = %s", procname); - /* ANALYZE */ - analyzeOid = atooid(PQgetvalue(res, 0, - PQfnumber(res, "typanalyzeoid"))); - if (OidIsValid(analyzeOid)) - appendPQExpBuffer(q, ",\n ANALYZE = %s", - PQgetvalue(res, 0, PQfnumber(res, "typanalyze"))); - - /* CANONICAL */ - canonicalOid = atooid(PQgetvalue(res, 0, - PQfnumber(res, "rngcanonicaloid"))); - if (OidIsValid(canonicalOid)) - appendPQExpBuffer(q, ",\n CANONICAL = %s", - PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"))); - - /* SUBTYPE_DIFF */ - subdiffOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "rngsubdiffoid"))); - if (OidIsValid(subdiffOid)) - appendPQExpBuffer(q, ",\n SUBTYPE_DIFF = %s", - PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"))); + procname = PQgetvalue(res, 0, PQfnumber(res, "typanalyze")); + if (strcmp(procname, "-") != 0) + appendPQExpBuffer(q, ",\n analyze = %s", procname); appendPQExpBuffer(q, "\n);\n"); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6449eca52d..9e277c5a1e 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2241,28 +2241,28 @@ typedef struct CreateEnumStmt } CreateEnumStmt; /* ---------------------- - * Alter Type Statement, enum types + * Create Type Statement, range types * ---------------------- */ -typedef struct AlterEnumStmt +typedef struct CreateRangeStmt { NodeTag type; List *typeName; /* qualified name (list of Value strings) */ - char *newVal; /* new enum value's name */ - char *newValNeighbor; /* neighboring enum value, if specified */ - bool newValIsAfter; /* place new enum value after neighbor? */ -} AlterEnumStmt; + List *params; /* range parameters (list of DefElem) */ +} CreateRangeStmt; /* ---------------------- - * Create Type Statement, range types + * Alter Type Statement, enum types * ---------------------- */ -typedef struct CreateRangeStmt +typedef struct AlterEnumStmt { NodeTag type; List *typeName; /* qualified name (list of Value strings) */ - List *params; /* range parameters (list of DefElem) */ -} CreateRangeStmt; + char *newVal; /* new enum value's name */ + char *newValNeighbor; /* neighboring enum value, if specified */ + bool newValIsAfter; /* place new enum value after neighbor? */ +} AlterEnumStmt; /* ---------------------- * Create View Statement diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 8a1c82ef72..47a14125c4 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -502,6 +502,8 @@ extern Datum anynonarray_in(PG_FUNCTION_ARGS); extern Datum anynonarray_out(PG_FUNCTION_ARGS); extern Datum anyenum_in(PG_FUNCTION_ARGS); extern Datum anyenum_out(PG_FUNCTION_ARGS); +extern Datum anyrange_in(PG_FUNCTION_ARGS); +extern Datum anyrange_out(PG_FUNCTION_ARGS); extern Datum void_in(PG_FUNCTION_ARGS); extern Datum void_out(PG_FUNCTION_ARGS); extern Datum void_recv(PG_FUNCTION_ARGS); diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h index 585d32134c..7d826d5521 100644 --- a/src/include/utils/rangetypes.h +++ b/src/include/utils/rangetypes.h @@ -73,9 +73,7 @@ typedef struct * prototypes for functions defined in rangetypes.c */ -/* IO */ -extern Datum anyrange_in(PG_FUNCTION_ARGS); -extern Datum anyrange_out(PG_FUNCTION_ARGS); +/* I/O */ extern Datum range_in(PG_FUNCTION_ARGS); extern Datum range_out(PG_FUNCTION_ARGS); extern Datum range_recv(PG_FUNCTION_ARGS); diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 04d8b532c3..275cad7b5c 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -2378,7 +2378,7 @@ compute_function_hashkey(FunctionCallInfo fcinfo, /* * This is the same as the standard resolve_polymorphic_argtypes() function, * but with a special case for validation: assume that polymorphic arguments - * are integer, integer-range or integer-array. Also, we go ahead and report + * are integer, integer-array or integer-range. Also, we go ahead and report * the error if we can't resolve the types. */ static void @@ -2412,12 +2412,12 @@ plpgsql_resolve_polymorphic_argtypes(int numargs, case ANYENUMOID: /* XXX dubious */ argtypes[i] = INT4OID; break; - case ANYRANGEOID: - argtypes[i] = INT4RANGEOID; - break; case ANYARRAYOID: argtypes[i] = INT4ARRAYOID; break; + case ANYRANGEOID: + argtypes[i] = INT4RANGEOID; + break; default: break; }