diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 3802aa24df..c64b040e45 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -1,4 +1,4 @@ - + Data Types @@ -3676,12 +3676,16 @@ SELECT * FROM pg_attribute any + + anyelement + + anyarray - anyelement + anynonarray @@ -3760,6 +3764,12 @@ 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. @@ -3813,7 +3823,7 @@ SELECT * FROM pg_attribute 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 and anyenum. + anyelement, anyenum, and anynonarray. diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index bb5834e74a..28bdfb8532 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -1,4 +1,4 @@ - + Extending <acronym>SQL</acronym> @@ -193,8 +193,8 @@ - Three pseudo-types of special interest are anyelement, - anyarray, and anyenum, + Four pseudo-types of special interest are anyelement, + anyarray, anynonarray, and anyenum, which are collectively called polymorphic types. Any function declared using these types is said to be a polymorphic function. A polymorphic function can @@ -216,6 +216,9 @@ anyelement, the actual array type in the anyarray positions must be an array whose elements are the same type appearing in the anyelement positions. + anynonarray is treated exactly the same as anyelement, + but adds the additional constraint that the actual type must not be + an array type. anyenum is treated exactly the same as anyelement, but adds the additional constraint that the actual type must be an enum type. @@ -242,6 +245,15 @@ is that a function declared as f(anyarray) returns anyenum will only accept arrays of enum types. + + + Note that anynonarray and anyenum do not represent + separate type variables; they are the same type as + anyelement, just with an additional constraint. For + example, declaring a function as f(anyelement, anyenum) + is equivalent to declaring it as f(anyenum, anyenum): + both actual arguments have to be the same enum type. + diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index c2f3371dc4..387c4e81c8 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ - + Functions and Operators @@ -986,24 +986,36 @@ This section describes functions and operators for examining and manipulating string values. Strings in this context include values - of all the types character, character - varying, and text. Unless otherwise noted, all + of the types character, character varying, + and text. Unless otherwise noted, all of the functions listed below work on all of these types, but be - wary of potential effects of the automatic padding when using the - character type. Generally, the functions described - here also work on data of non-string types by converting that data - to a string representation first. Some functions also exist + wary of potential effects of automatic space-padding when using the + character type. Some functions also exist natively for the bit-string types. - SQL defines some string functions with a special syntax where - certain key words rather than commas are used to separate the + SQL defines some string functions with a special syntax + wherein certain key words rather than commas are used to separate the arguments. Details are in . These functions are also implemented using the regular syntax for function invocation. (See .) + + + Before PostgreSQL 8.3, these functions would + silently accept values of several non-string data types as well, due to + the presence of implicit coercions from those data types to + text. Those coercions have been removed because they frequently + caused surprising behaviors. However, the string concatenation operator + (||) still accepts non-string input, so long as at least one + input is of a string type, as shown in . For other cases, insert an explicit + coercion to text if you need to duplicate the previous behavior. + + + bit_length @@ -1064,6 +1076,22 @@ PostgreSQL + + + string || + non-string + or + non-string || + string + + text + + String concatenation with one non-string input + + 'Value: ' || 42 + Value: 42 + + bit_length(string) int diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index c1f57ddf4f..d14519b613 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ - + <application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language @@ -210,8 +210,8 @@ $$ LANGUAGE plpgsql; PL/pgSQL functions can also be declared to accept and return the polymorphic types - anyelement, anyarray, and anyenum. - The actual + anyelement, anyarray, anynonarray, + and anyenum. The actual data types handled by a polymorphic function can vary from call to call, as discussed in . An example is shown in . @@ -700,7 +700,7 @@ $$ LANGUAGE plpgsql; When the return type of a PL/pgSQL function is declared as a polymorphic type (anyelement, - anyarray, or anyenum), + anyarray, anynonarray, or anyenum), a special parameter $0 is created. Its data type is the actual return type of the function, as deduced from the actual input types (see + User-Defined Functions @@ -718,7 +718,8 @@ SELECT name, listchildren(name) FROM nodes; SQL functions can be declared to accept and return the polymorphic types anyelement, - anyarray, and anyenum. See anyarray, anynonarray, and + anyenum. See for a more detailed explanation of polymorphic functions. Here is a polymorphic function make_array that builds up an array @@ -2831,7 +2832,8 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer, C-language functions can be declared to accept and return the polymorphic types - anyelement, anyarray, and anyenum. + anyelement, anyarray, anynonarray, + and anyenum. See for a more detailed explanation of polymorphic functions. When function arguments or return types are defined as polymorphic types, the function author cannot know diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 26c5eb75e3..9db3ef4edb 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.144 2007/04/02 03:49:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.145 2007/06/06 23:00:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -147,6 +147,7 @@ ProcedureCreate(const char *procedureName, { case ANYARRAYOID: case ANYELEMENTOID: + case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; @@ -170,6 +171,7 @@ ProcedureCreate(const char *procedureName, { case ANYARRAYOID: case ANYELEMENTOID: + case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 9ad1391612..8beea3f539 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.116 2007/04/27 22:05:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.117 2007/06/06 23:00:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -849,9 +849,9 @@ ShutdownSQLFunction(Datum arg) * to be sure that the user is returning the type he claims. * * For a polymorphic function the passed rettype must be the actual resolved - * output type of the function; we should never see ANYARRAY, ANYENUM or - * ANYELEMENT as rettype. (This means we can't check the type during function - * definition of a polymorphic function.) + * output type of the function; we should never see a polymorphic pseudotype + * such as ANYELEMENT as rettype. (This means we can't check the type during + * function definition of a polymorphic function.) * * This function returns true if the sql function returns the entire tuple * result of its final SELECT, and false otherwise. Note that because we @@ -874,6 +874,8 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, char fn_typtype; Oid restype; + AssertArg(!IsPolymorphicType(rettype)); + if (junkFilter) *junkFilter = NULL; /* default result */ diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 1409285cda..1afc55bc9a 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.77 2007/02/01 19:10:27 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.78 2007/06/06 23:00:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -363,7 +363,7 @@ check_ungrouped_columns_walker(Node *node, * * agg_input_types, agg_state_type, agg_result_type identify the input, * transition, and result types of the aggregate. These should all be - * resolved to actual types (ie, none should ever be ANYARRAY or ANYELEMENT). + * resolved to actual types (ie, none should ever be ANYELEMENT etc). * * transfn_oid and finalfn_oid identify the funcs to be called; the latter * may be InvalidOid. diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 98cc669112..abc0568581 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.154 2007/06/05 21:31:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.155 2007/06/06 23:00:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -133,6 +133,7 @@ coerce_type(ParseState *pstate, Node *node, } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || + targetTypeId == ANYNONARRAYOID || (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) || (targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID)) { @@ -141,12 +142,12 @@ coerce_type(ParseState *pstate, Node *node, * * Note: by returning the unmodified node here, we are saying that * it's OK to treat an UNKNOWN constant as a valid input for a - * function accepting ANY or ANYELEMENT. This should be all right, - * since an UNKNOWN value is still a perfectly valid Datum. However - * an UNKNOWN value is definitely *not* an array, and so we mustn't - * accept it for ANYARRAY. (Instead, we will call anyarray_in below, - * which will produce an error.) Likewise, UNKNOWN input is no good - * for ANYENUM. + * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be + * all right, since an UNKNOWN value is still a perfectly valid Datum. + * However an UNKNOWN value is definitely *not* an array, and so we + * mustn't accept it for ANYARRAY. (Instead, we will call anyarray_in + * below, which will produce an error.) Likewise, UNKNOWN input is no + * good for ANYENUM. * * NB: we do NOT want a RelabelType here. */ @@ -1078,9 +1079,13 @@ coerce_to_common_type(ParseState *pstate, Node *node, * 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.) * - * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT - * or ANYARRAY argument, assume it is okay. + * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic + * argument, assume it is okay. * * If an input is of type ANYARRAY (ie, we know it's an array, but not * what element type), we will accept it as a match to an argument declared @@ -1100,21 +1105,26 @@ check_generic_type_consistency(Oid *actual_arg_types, Oid array_typeid = InvalidOid; Oid array_typelem; bool have_anyelement = false; + bool have_anynonarray = false; bool have_anyenum = false; /* - * Loop through the arguments to see if we have any that are ANYARRAY or - * ANYELEMENT. If so, require the actual types to be self-consistent + * Loop through the arguments to see if we have any that are polymorphic. + * If so, require the actual types to be consistent. */ for (j = 0; j < nargs; j++) { + Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; - if (declared_arg_types[j] == ANYELEMENTOID || - declared_arg_types[j] == ANYENUMOID) + if (decl_type == ANYELEMENTOID || + decl_type == ANYNONARRAYOID || + decl_type == ANYENUMOID) { have_anyelement = true; - if (declared_arg_types[j] == ANYENUMOID) + if (decl_type == ANYNONARRAYOID) + have_anynonarray = true; + else if (decl_type == ANYENUMOID) have_anyenum = true; if (actual_type == UNKNOWNOID) continue; @@ -1122,7 +1132,7 @@ check_generic_type_consistency(Oid *actual_arg_types, return false; elem_typeid = actual_type; } - else if (declared_arg_types[j] == ANYARRAYOID) + else if (decl_type == ANYARRAYOID) { if (actual_type == UNKNOWNOID) continue; @@ -1161,6 +1171,13 @@ check_generic_type_consistency(Oid *actual_arg_types, } } + if (have_anynonarray) + { + /* require the element type to not be an array */ + if (type_is_array(elem_typeid)) + return false; + } + if (have_anyenum) { /* require the element type to be an enum */ @@ -1177,7 +1194,7 @@ check_generic_type_consistency(Oid *actual_arg_types, * Make sure a polymorphic function is legally callable, and * deduce actual argument and result types. * - * If ANYARRAY, ANYELEMENT, or ANYENUM is used for a function's arguments or + * 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 * check_generic_type_consistency(). @@ -1211,6 +1228,10 @@ check_generic_type_consistency(Oid *actual_arg_types, * 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.) */ Oid enforce_generic_type_consistency(Oid *actual_arg_types, @@ -1225,22 +1246,28 @@ enforce_generic_type_consistency(Oid *actual_arg_types, Oid array_typeid = InvalidOid; Oid array_typelem; bool have_anyelement = (rettype == ANYELEMENTOID || + rettype == ANYNONARRAYOID || rettype == ANYENUMOID); + bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anyenum = (rettype == ANYENUMOID); /* - * Loop through the arguments to see if we have any that are ANYARRAY or - * ANYELEMENT. If so, require the actual types to be self-consistent + * Loop through the arguments to see if we have any that are polymorphic. + * If so, require the actual types to be consistent. */ for (j = 0; j < nargs; j++) { + Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; - if (declared_arg_types[j] == ANYELEMENTOID || - declared_arg_types[j] == ANYENUMOID) + if (decl_type == ANYELEMENTOID || + decl_type == ANYNONARRAYOID || + decl_type == ANYENUMOID) { have_generics = have_anyelement = true; - if (declared_arg_types[j] == ANYENUMOID) + if (decl_type == ANYNONARRAYOID) + have_anynonarray = true; + else if (decl_type == ANYENUMOID) have_anyenum = true; if (actual_type == UNKNOWNOID) { @@ -1256,7 +1283,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, format_type_be(actual_type)))); elem_typeid = actual_type; } - else if (declared_arg_types[j] == ANYARRAYOID) + else if (decl_type == ANYARRAYOID) { have_generics = true; if (actual_type == UNKNOWNOID) @@ -1326,6 +1353,16 @@ enforce_generic_type_consistency(Oid *actual_arg_types, errmsg("could not determine polymorphic type because input has type \"unknown\""))); } + if (have_anynonarray) + { + /* require the element type to not be an array */ + if (type_is_array(elem_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anynonarray is an array type: %s", + format_type_be(elem_typeid)))); + } + if (have_anyenum) { /* require the element type to be an enum */ @@ -1343,15 +1380,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types, { for (j = 0; j < nargs; j++) { + Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; if (actual_type != UNKNOWNOID) continue; - if (declared_arg_types[j] == ANYELEMENTOID || - declared_arg_types[j] == ANYENUMOID) + if (decl_type == ANYELEMENTOID || + decl_type == ANYNONARRAYOID || + decl_type == ANYENUMOID) declared_arg_types[j] = elem_typeid; - else if (declared_arg_types[j] == ANYARRAYOID) + else if (decl_type == ANYARRAYOID) { if (!OidIsValid(array_typeid)) { @@ -1383,7 +1422,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types, } /* if we return ANYELEMENT use the appropriate argument type */ - if (rettype == ANYELEMENTOID || rettype == ANYENUMOID) + if (rettype == ANYELEMENTOID || + rettype == ANYNONARRAYOID || + rettype == ANYENUMOID) return elem_typeid; /* we don't return a generic type; send back the original return type */ @@ -1423,6 +1464,7 @@ resolve_generic_type(Oid declared_type, return context_actual_type; } else if (context_declared_type == ANYELEMENTOID || + context_declared_type == ANYNONARRAYOID || context_declared_type == ANYENUMOID) { /* Use the array type corresponding to actual type */ @@ -1436,7 +1478,9 @@ resolve_generic_type(Oid declared_type, return array_typeid; } } - else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID) + else if (declared_type == ANYELEMENTOID || + declared_type == ANYNONARRAYOID || + declared_type == ANYENUMOID) { if (context_declared_type == ANYARRAYOID) { @@ -1451,6 +1495,7 @@ resolve_generic_type(Oid declared_type, return array_typelem; } else if (context_declared_type == ANYELEMENTOID || + context_declared_type == ANYNONARRAYOID || context_declared_type == ANYENUMOID) { /* Use the actual type; it doesn't matter if array or not */ @@ -1564,6 +1609,7 @@ TypeCategory(Oid inType) case (INTERNALOID): case (OPAQUEOID): case (ANYELEMENTOID): + case (ANYNONARRAYOID): case (ANYENUMOID): result = GENERIC_TYPE; break; @@ -1707,7 +1753,12 @@ IsBinaryCoercible(Oid srctype, Oid targettype) /* Also accept any array type as coercible to ANYARRAY */ if (targettype == ANYARRAYOID) - if (get_element_type(srctype) != InvalidOid) + if (type_is_array(srctype)) + return true; + + /* Also accept any non-array type as coercible to ANYNONARRAY */ + if (targettype == ANYNONARRAYOID) + if (!type_is_array(srctype)) return true; /* Also accept any enum type as coercible to ANYENUM */ @@ -1863,22 +1914,6 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, } } - /* - * If we still haven't found a possibility, check for enums, - * and retry looking for a cast to or from ANYENUM. But don't - * mistakenly conclude that ANYENUM-to-some-enum-type is a - * trivial cast. - */ - if (result == COERCION_PATH_NONE) - { - if (type_is_enum(sourceTypeId)) - result = find_coercion_pathway(targetTypeId, ANYENUMOID, - ccontext, funcid); - else if (sourceTypeId != ANYENUMOID && type_is_enum(targetTypeId)) - result = find_coercion_pathway(ANYENUMOID, sourceTypeId, - ccontext, funcid); - } - /* * If we still haven't found a possibility, consider automatic casting * using I/O functions. We allow assignment casts to textual types diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 5e7a1cccff..393fa6c41a 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.196 2007/06/05 21:31:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.197 2007/06/06 23:00:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -227,9 +227,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, } /* - * enforce consistency with ANYARRAY and ANYELEMENT argument and return - * types, possibly adjusting return type or declared_arg_types (which will - * be used as the cast destination by make_fn_arguments) + * enforce consistency with polymorphic argument and return types, + * possibly adjusting return type or declared_arg_types (which will be + * used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index fc9f332482..248bcfa305 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.19 2007/04/02 03:49:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.20 2007/06/06 23:00:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -347,6 +347,32 @@ anyelement_out(PG_FUNCTION_ARGS) PG_RETURN_VOID(); /* keep compiler quiet */ } +/* + * anynonarray_in - input routine for pseudo-type ANYNONARRAY. + */ +Datum +anynonarray_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type anynonarray"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * anynonarray_out - output routine for pseudo-type ANYNONARRAY. + */ +Datum +anynonarray_out(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot display a value of type anynonarray"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + /* * shell_in - input routine for "shell" types (those not yet filled in). */ diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index f9fd48db66..2e84fac1d7 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.43 2007/05/21 17:10:29 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.44 2007/06/06 23:00:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1481,7 +1481,7 @@ map_sql_value_to_xml_value(Datum value, Oid type) initStringInfo(&buf); - if (is_array_type(type)) + if (type_is_array(type)) { int i; ArrayType *array; diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index c32130c3ec..53a505858b 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -7,7 +7,7 @@ * Copyright (c) 2002-2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.34 2007/04/02 03:49:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.35 2007/06/06 23:00:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -193,8 +193,8 @@ shutdown_MultiFuncCall(Datum arg) * only when we couldn't resolve the actual rowtype for lack of information. * * The other hard case that this handles is resolution of polymorphism. - * We will never return ANYELEMENT, ANYARRAY or ANYENUM, either as a scalar - * result type or as a component of a rowtype. + * We will never return polymorphic pseudotypes (ANYELEMENT etc), either + * as a scalar result type or as a component of a rowtype. * * This function is relatively expensive --- in a function returning set, * try to call it only the first time through. @@ -389,9 +389,9 @@ internal_get_result_type(Oid funcid, /* * Given the result tuple descriptor for a function with OUT parameters, - * replace any polymorphic columns (ANYELEMENT/ANYARRAY/ANYENUM) with correct - * data types deduced from the input arguments. Returns TRUE if able to deduce - * all types, FALSE if not. + * replace any polymorphic columns (ANYELEMENT etc) with correct data types + * deduced from the input arguments. Returns TRUE if able to deduce all types, + * FALSE if not. */ static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, @@ -401,6 +401,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, int nargs = declared_args->dim1; bool have_anyelement_result = false; bool have_anyarray_result = false; + bool have_anynonarray = false; bool have_anyenum = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; @@ -417,6 +418,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, case ANYARRAYOID: have_anyarray_result = true; break; + case ANYNONARRAYOID: + have_anyelement_result = true; + have_anynonarray = true; + break; case ANYENUMOID: have_anyelement_result = true; have_anyenum = true; @@ -440,6 +445,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, switch (declared_args->values[i]) { case ANYELEMENTOID: + case ANYNONARRAYOID: case ANYENUMOID: if (!OidIsValid(anyelement_type)) anyelement_type = get_call_expr_argtype(call_expr, i); @@ -467,7 +473,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, anyelement_type, ANYELEMENTOID); - /* Check for enum if needed */ + /* Enforce ANYNONARRAY if needed */ + if (have_anynonarray && type_is_array(anyelement_type)) + return false; + + /* Enforce ANYENUM if needed */ if (have_anyenum && !type_is_enum(anyelement_type)) return false; @@ -477,6 +487,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: + case ANYNONARRAYOID: case ANYENUMOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), @@ -500,11 +511,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, } /* - * Given the declared argument types and modes for a function, - * replace any polymorphic types (ANYELEMENT/ANYARRAY/ANYENUM) with correct - * data types deduced from the input arguments. Returns TRUE if able to deduce - * all types, FALSE if not. This is the same logic as - * resolve_polymorphic_tupdesc, but with a different argument representation. + * Given the declared argument types and modes for a function, replace any + * polymorphic types (ANYELEMENT etc) with correct data types deduced from the + * input arguments. Returns TRUE if able to deduce all types, FALSE if not. + * This is the same logic as resolve_polymorphic_tupdesc, but with a different + * argument representation. * * argmodes may be NULL, in which case all arguments are assumed to be IN mode. */ @@ -528,6 +539,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, switch (argtypes[i]) { case ANYELEMENTOID: + case ANYNONARRAYOID: case ANYENUMOID: if (argmode == PROARGMODE_OUT) have_anyelement_result = true; @@ -583,7 +595,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, anyelement_type, ANYELEMENTOID); - /* XXX do we need to enforce ANYENUM here? I think not */ + /* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */ /* And finally replace the output column types as needed */ for (i = 0; i < numargs; i++) @@ -591,6 +603,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, switch (argtypes[i]) { case ANYELEMENTOID: + case ANYNONARRAYOID: case ANYENUMOID: argtypes[i] = anyelement_type; break; diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index ec6d516713..8dfca8ecc7 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.410 2007/06/05 21:31:07 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.411 2007/06/06 23:00:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200706051 +#define CATALOG_VERSION_NO 200706061 #endif diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 6848b274e4..e3b2910eb2 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.152 2007/05/08 18:56:47 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.153 2007/06/06 23:00:41 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -701,7 +701,7 @@ DATA(insert OID = 1793 ( "#" PGNSP PGUID b f f 1560 1560 1560 1793 0 bitxor DATA(insert OID = 1794 ( "~" PGNSP PGUID l f f 0 1560 1560 0 0 bitnot - - )); DATA(insert OID = 1795 ( "<<" PGNSP PGUID b f f 1560 23 1560 0 0 bitshiftleft - - )); DATA(insert OID = 1796 ( ">>" PGNSP PGUID b f f 1560 23 1560 0 0 bitshiftright - - )); -DATA(insert OID = 1797 ( "||" PGNSP PGUID b f f 1560 1560 1560 0 0 bitcat - - )); +DATA(insert OID = 1797 ( "||" PGNSP PGUID b f f 1562 1562 1562 0 0 bitcat - - )); DATA(insert OID = 1800 ( "+" PGNSP PGUID b f f 1083 1186 1083 1849 0 time_pl_interval - - )); DATA(insert OID = 1801 ( "-" PGNSP PGUID b f f 1083 1186 1083 0 0 time_mi_interval - - )); @@ -875,6 +875,10 @@ DATA(insert OID = 2750 ( "&&" PGNSP PGUID b f f 2277 2277 16 2750 0 arrayov DATA(insert OID = 2751 ( "@>" PGNSP PGUID b f f 2277 2277 16 2752 0 arraycontains contsel contjoinsel )); DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained contsel contjoinsel )); +/* capturing operators to preserve pre-8.3 behavior of text concatenation */ +DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - )); +DATA(insert OID = 2780 ( "||" PGNSP PGUID b f f 2776 25 25 0 0 anytextcat - - )); + /* obsolete names for contains/contained-by operators; remove these someday */ DATA(insert OID = 2860 ( "@" PGNSP PGUID b f f 604 604 16 2861 0 poly_contained contsel contjoinsel )); DATA(insert OID = 2861 ( "~" PGNSP PGUID b f f 604 604 16 2860 0 poly_contain contsel contjoinsel )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c656c7a671..49c1429e37 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.458 2007/06/05 21:31:07 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.459 2007/06/06 23:00:41 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1425,7 +1425,7 @@ DATA(insert OID = 1156 ( timestamptz_ge PGNSP PGUID 12 1 0 f f t f i 2 16 "11 DESCR("greater-than-or-equal"); DATA(insert OID = 1157 ( timestamptz_gt PGNSP PGUID 12 1 0 f f t f i 2 16 "1184 1184" _null_ _null_ _null_ timestamp_gt - _null_ )); DESCR("greater-than"); -DATA(insert OID = 1158 ( to_timestamp PGNSP PGUID 14 1 0 f f t f i 1 1184 "701" _null_ _null_ _null_ "select (''epoch''::timestamptz + $1 * ''1 second''::interval)" - _null_ )); +DATA(insert OID = 1158 ( to_timestamp PGNSP PGUID 14 1 0 f f t f i 1 1184 "701" _null_ _null_ _null_ "select (''epoch''::pg_catalog.timestamptz + $1 * ''1 second''::pg_catalog.interval)" - _null_ )); DESCR("convert UNIX epoch to timestamptz"); DATA(insert OID = 1159 ( timezone PGNSP PGUID 12 1 0 f f t f i 2 1114 "25 1184" _null_ _null_ _null_ timestamptz_zone - _null_ )); DESCR("adjust timestamp to new time zone"); @@ -1509,7 +1509,7 @@ DESCR("adjust interval precision"); DATA(insert OID = 1215 ( obj_description PGNSP PGUID 14 100 0 f f t f s 2 25 "26 19" _null_ _null_ _null_ "select description from pg_catalog.pg_description where objoid = $1 and classoid = (select oid from pg_catalog.pg_class where relname = $2 and relnamespace = PGNSP) and objsubid = 0" - _null_ )); DESCR("get description for object id and catalog name"); -DATA(insert OID = 1216 ( col_description PGNSP PGUID 14 100 0 f f t f s 2 25 "26 23" _null_ _null_ _null_ "select description from pg_catalog.pg_description where objoid = $1 and classoid = ''pg_catalog.pg_class''::regclass and objsubid = $2" - _null_ )); +DATA(insert OID = 1216 ( col_description PGNSP PGUID 14 100 0 f f t f s 2 25 "26 23" _null_ _null_ _null_ "select description from pg_catalog.pg_description where objoid = $1 and classoid = ''pg_catalog.pg_class''::pg_catalog.regclass and objsubid = $2" - _null_ )); DESCR("get description for table column"); DATA(insert OID = 1993 ( shobj_description PGNSP PGUID 14 100 0 f f t f s 2 25 "26 19" _null_ _null_ _null_ "select description from pg_catalog.pg_shdescription where objoid = $1 and classoid = (select oid from pg_catalog.pg_class where relname = $2 and relnamespace = PGNSP)" - _null_ )); DESCR("get description for object id and shared catalog name"); @@ -1722,7 +1722,7 @@ DESCR("less-equal-greater"); DATA(insert OID = 1359 ( timestamptz PGNSP PGUID 12 1 0 f f t f i 2 1184 "1082 1266" _null_ _null_ _null_ datetimetz_timestamptz - _null_ )); DESCR("convert date and time with time zone to timestamp with time zone"); -DATA(insert OID = 1364 ( time PGNSP PGUID 14 1 0 f f t f s 1 1083 "702" _null_ _null_ _null_ "select cast(cast($1 as timestamp without time zone) as time)" - _null_ )); +DATA(insert OID = 1364 ( time PGNSP PGUID 14 1 0 f f t f s 1 1083 "702" _null_ _null_ _null_ "select cast(cast($1 as timestamp without time zone) as pg_catalog.time)" - _null_ )); DESCR("convert abstime to time"); DATA(insert OID = 1367 ( character_length PGNSP PGUID 12 1 0 f f t f i 1 23 "1042" _null_ _null_ _null_ bpcharlen - _null_ )); @@ -2328,7 +2328,7 @@ DATA(insert OID = 1677 ( bitshiftleft PGNSP PGUID 12 1 0 f f t f i 2 1560 "156 DESCR("bitwise left shift"); DATA(insert OID = 1678 ( bitshiftright PGNSP PGUID 12 1 0 f f t f i 2 1560 "1560 23" _null_ _null_ _null_ bitshiftright - _null_ )); DESCR("bitwise right shift"); -DATA(insert OID = 1679 ( bitcat PGNSP PGUID 12 1 0 f f t f i 2 1560 "1560 1560" _null_ _null_ _null_ bitcat - _null_ )); +DATA(insert OID = 1679 ( bitcat PGNSP PGUID 12 1 0 f f t f i 2 1562 "1562 1562" _null_ _null_ _null_ bitcat - _null_ )); DESCR("bitwise concatenation"); DATA(insert OID = 1680 ( substring PGNSP PGUID 12 1 0 f f t f i 3 1560 "1560 23 23" _null_ _null_ _null_ bitsubstr - _null_ )); DESCR("return portion of bitstring"); @@ -2981,6 +2981,11 @@ DESCR("adjust time precision"); DATA(insert OID = 1969 ( timetz PGNSP PGUID 12 1 0 f f t f i 2 1266 "1266 23" _null_ _null_ _null_ timetz_scale - _null_ )); DESCR("adjust time with time zone precision"); +DATA(insert OID = 2003 ( textanycat PGNSP PGUID 14 1 0 f f t f i 2 25 "25 2776" _null_ _null_ _null_ "select $1 || $2::pg_catalog.text" - _null_ )); +DESCR("concatenate"); +DATA(insert OID = 2004 ( anytextcat PGNSP PGUID 14 1 0 f f t f i 2 25 "2776 25" _null_ _null_ _null_ "select $1::pg_catalog.text || $2" - _null_ )); +DESCR("concatenate"); + DATA(insert OID = 2005 ( bytealike PGNSP PGUID 12 1 0 f f t f i 2 16 "17 17" _null_ _null_ _null_ bytealike - _null_ )); DESCR("matches LIKE expression"); DATA(insert OID = 2006 ( byteanlike PGNSP PGUID 12 1 0 f f t f i 2 16 "17 17" _null_ _null_ _null_ byteanlike - _null_ )); @@ -3489,6 +3494,10 @@ DATA(insert OID = 2597 ( domain_in PGNSP PGUID 12 1 0 f f f f v 3 2276 "2275 DESCR("I/O"); DATA(insert OID = 2598 ( domain_recv PGNSP PGUID 12 1 0 f f f f v 3 2276 "2281 26 23" _null_ _null_ _null_ domain_recv - _null_ )); DESCR("I/O"); +DATA(insert OID = 2777 ( anynonarray_in PGNSP PGUID 12 1 0 f f t f i 1 2776 "2275" _null_ _null_ _null_ anynonarray_in - _null_ )); +DESCR("I/O"); +DATA(insert OID = 2778 ( anynonarray_out PGNSP PGUID 12 1 0 f f t f i 1 2275 "2776" _null_ _null_ _null_ anynonarray_out - _null_ )); +DESCR("I/O"); /* cryptographic */ DATA(insert OID = 2311 ( md5 PGNSP PGUID 12 1 0 f f t f i 1 25 "25" _null_ _null_ _null_ md5_text - _null_ )); @@ -4054,7 +4063,7 @@ DESCR("map database contents and structure to XML and XML Schema"); DATA(insert OID = 2931 ( xpath PGNSP PGUID 12 1 0 f f t f i 3 143 "25 142 1009" _null_ _null_ _null_ xpath - _null_ )); DESCR("evaluate XPath expression, with namespaces support"); -DATA(insert OID = 2932 ( xpath PGNSP PGUID 14 1 0 f f t f i 2 143 "25 142" _null_ _null_ _null_ "select pg_catalog.xpath($1, $2, ''{}''::_text)" - _null_ )); +DATA(insert OID = 2932 ( xpath PGNSP PGUID 14 1 0 f f t f i 2 143 "25 142" _null_ _null_ _null_ "select pg_catalog.xpath($1, $2, ''{}''::pg_catalog.text[])" - _null_ )); DESCR("evaluate XPath expression"); /* uuid */ diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 0d9ff0d6ee..753487348b 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.184 2007/05/12 00:54:59 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.185 2007/06/06 23:00:43 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -575,6 +575,8 @@ DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p t \054 0 0 0 opaque_in opaq #define OPAQUEOID 2282 DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ )); #define ANYELEMENTOID 2283 +DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ )); +#define ANYNONARRAYOID 2776 DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ )); #define ANYENUMOID 3500 @@ -592,6 +594,7 @@ DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p t \054 0 0 0 anyenum_in any #define IsPolymorphicType(typid) \ ((typid) == ANYELEMENTOID || \ (typid) == ANYARRAYOID || \ + (typid) == ANYNONARRAYOID || \ (typid) == ANYENUMOID) /* diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 5ff4fe738f..faf8b69262 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.295 2007/06/05 21:31:08 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.296 2007/06/06 23:00:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -458,6 +458,8 @@ extern Datum anyarray_in(PG_FUNCTION_ARGS); extern Datum anyarray_out(PG_FUNCTION_ARGS); extern Datum anyarray_recv(PG_FUNCTION_ARGS); extern Datum anyarray_send(PG_FUNCTION_ARGS); +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 void_in(PG_FUNCTION_ARGS); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 25782e322e..0a326cb9ba 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.118 2007/04/02 03:49:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.119 2007/06/06 23:00:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -130,7 +130,7 @@ extern char *get_namespace_name(Oid nspid); extern Oid get_roleid(const char *rolname); extern Oid get_roleid_checked(const char *rolname); -#define is_array_type(typid) (get_element_type(typid) != InvalidOid) +#define type_is_array(typid) (get_element_type(typid) != InvalidOid) #define TypeIsToastable(typid) (get_typstorage(typid) != 'p') diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 74918c890c..a547d174c1 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.114 2007/04/02 03:49:41 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.115 2007/06/06 23:00:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -480,7 +480,7 @@ do_compile(FunctionCallInfo fcinfo, { if (rettypeid == ANYARRAYOID) rettypeid = INT4ARRAYOID; - else + else /* ANYELEMENT or ANYNONARRAY */ rettypeid = INT4OID; /* XXX what could we use for ANYENUM? */ } @@ -2027,6 +2027,7 @@ plpgsql_resolve_polymorphic_argtypes(int numargs, switch (argtypes[i]) { case ANYELEMENTOID: + case ANYNONARRAYOID: case ANYENUMOID: /* XXX dubious */ argtypes[i] = INT4OID; break; diff --git a/src/test/regress/expected/text.out b/src/test/regress/expected/text.out index 2d732f6844..08d002fe71 100644 --- a/src/test/regress/expected/text.out +++ b/src/test/regress/expected/text.out @@ -23,3 +23,31 @@ SELECT '' AS two, * FROM TEXT_TBL; | hi de ho neighbor (2 rows) +-- As of 8.3 we have removed most implicit casts to text, so that for example +-- this no longer works: +select length(42); +ERROR: function length(integer) does not exist +LINE 1: select length(42); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- But as a special exception for usability's sake, we still allow implicit +-- casting to text in concatenations, so long as the other input is text or +-- an unknown literal. So these work: +select 'four: '::text || 2+2; + ?column? +---------- + four: 4 +(1 row) + +select 'four: ' || 2+2; + ?column? +---------- + four: 4 +(1 row) + +-- but not this: +select 3 || 4.0; +ERROR: operator does not exist: integer || numeric +LINE 1: select 3 || 4.0; + ^ +HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts. diff --git a/src/test/regress/sql/text.sql b/src/test/regress/sql/text.sql index 60daf7077c..b739e56e2d 100644 --- a/src/test/regress/sql/text.sql +++ b/src/test/regress/sql/text.sql @@ -13,3 +13,18 @@ INSERT INTO TEXT_TBL VALUES ('hi de ho neighbor'); SELECT '' AS two, * FROM TEXT_TBL; +-- As of 8.3 we have removed most implicit casts to text, so that for example +-- this no longer works: + +select length(42); + +-- But as a special exception for usability's sake, we still allow implicit +-- casting to text in concatenations, so long as the other input is text or +-- an unknown literal. So these work: + +select 'four: '::text || 2+2; +select 'four: ' || 2+2; + +-- but not this: + +select 3 || 4.0;