diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 226eef1c8dae43ddc912e470a081ba6d2d95a0f9..84ae3cb205f543b56d909a0125ac3becd92ec7a3 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ <!-- Documentation of the system catalogs, directed toward PostgreSQL developers - $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.86 2004/06/07 04:04:47 tgl Exp $ + $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.87 2004/06/16 01:26:33 tgl Exp $ --> <chapter id="catalogs"> @@ -934,7 +934,7 @@ <entry> Indicates what contexts the cast may be invoked in. <literal>e</> means only as an explicit cast (using - <literal>CAST</>, <literal>::</>, or function-call syntax). + <literal>CAST</> or <literal>::</> syntax). <literal>a</> means implicitly in assignment to a target column, as well as explicitly. <literal>i</> means implicitly in expressions, as well as the @@ -944,6 +944,39 @@ </tbody> </tgroup> </table> + + <para> + The cast functions listed in <structname>pg_cast</structname> must + always take the cast source type as their first argument type, and + return the cast destination type as their result type. A cast + function can have up to three arguments. The second argument, + if present, must be type <type>integer</>; it receives the type + modifier associated with the destination type, or <literal>-1</> + if there is none. The third argument, + if present, must be type <type>boolean</>; it receives <literal>true</> + if the cast is an explicit cast, <literal>false</> otherwise. + </para> + + <para> + It is legitimate to create a <structname>pg_cast</structname> entry + in which the source and target types are the same, if the associated + function takes more than one argument. Such entries represent + <quote>length coercion functions</> that coerce values of the type + to be legal for a particular type modifier value. Note however that + at present there is no support for associating non-default type + modifiers with user-created data types, and so this facility is only + of use for the small number of built-in types that have type modifier + syntax built into the grammar. + </para> + + <para> + When a <structname>pg_cast</structname> entry has different source and + target types and a function that takes more than one argument, it + represents converting from one type to another and applying a length + coercion in a single step. When no such entry is available, coercion + to a type that uses a type modifier involves two steps, one to + convert between datatypes and a second to apply the modifier. + </para> </sect1> <sect1 id="catalog-pg-class"> diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 19feebe0b28eaac9bb72de7d2e7a7c5612bddc46..fdc63d8250ad3dcc1e5c59db2f7ccac798cd159f 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.145 2004/06/07 04:04:47 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.146 2004/06/16 01:26:35 tgl Exp $ --> <chapter id="datatype"> @@ -2851,7 +2851,7 @@ SELECT * FROM test1 WHERE a; linkend="sql-syntax-bit-strings"> for information about the syntax of bit string constants. Bit-logical operators and string manipulation functions are available; see <xref - linkend="functions">. + linkend="functions-bitstring">. </para> <example> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 37cc1411f817ad5e285e7ece405d89978f2cb165..3de1adafc9a786f6429931284a1d2a3a0fa34aa2 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.208 2004/06/14 19:01:09 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.209 2004/06/16 01:26:36 tgl Exp $ PostgreSQL documentation --> @@ -488,55 +488,13 @@ PostgreSQL documentation </table> <para> - The bitwise operators are also available for the bit + The bitwise operators work only on integral data types, whereas + the others are available for all numeric data types. The bitwise + operators are also available for the bit string types <type>bit</type> and <type>bit varying</type>, as - shown in <xref linkend="functions-math-bit-table">. - Bit string operands of <literal>&</literal>, <literal>|</literal>, - and <literal>#</literal> must be of equal length. When bit - shifting, the original length of the string is preserved, as shown - in the table. + shown in <xref linkend="functions-bit-string-op-table">. </para> - <table id="functions-math-bit-table"> - <title>Bit String Bitwise Operators</title> - - <tgroup cols="2"> - <thead> - <row> - <entry>Example</entry> - <entry>Result</entry> - </row> - </thead> - - <tbody> - <row> - <entry><literal>B'10001' & B'01101'</literal></entry> - <entry><literal>00001</literal></entry> - </row> - <row> - <entry><literal>B'10001' | B'01101'</literal></entry> - <entry><literal>11101</literal></entry> - </row> - <row> - <entry><literal>B'10001' # B'01101'</literal></entry> - <entry><literal>11110</literal></entry> - </row> - <row> - <entry><literal>~ B'10001'</literal></entry> - <entry><literal>01110</literal></entry> - </row> - <row> - <entry><literal>B'10001' << 3</literal></entry> - <entry><literal>01000</literal></entry> - </row> - <row> - <entry><literal>B'10001' >> 2</literal></entry> - <entry><literal>00100</literal></entry> - </row> - </tbody> - </tgroup> - </table> - <para> <xref linkend="functions-math-func-table"> shows the available mathematical functions. In the table, <literal>dp</literal> @@ -2337,6 +2295,130 @@ PostgreSQL documentation </sect1> + <sect1 id="functions-bitstring"> + <title>Bit String Functions and Operators</title> + + <indexterm zone="functions-bitstring"> + <primary>bit strings</primary> + <secondary>functions</secondary> + </indexterm> + + <para> + This section describes functions and operators for examining and + manipulating bit strings, that is values of the types + <type>bit</type> and <type>bit varying</type>. Aside from the + usual comparison operators, the operators + shown in <xref linkend="functions-bit-string-op-table"> can be used. + Bit string operands of <literal>&</literal>, <literal>|</literal>, + and <literal>#</literal> must be of equal length. When bit + shifting, the original length of the string is preserved, as shown + in the examples. + </para> + + <table id="functions-bit-string-op-table"> + <title>Bit String Operators</title> + + <tgroup cols="4"> + <thead> + <row> + <entry>Operator</entry> + <entry>Description</entry> + <entry>Example</entry> + <entry>Result</entry> + </row> + </thead> + + <tbody> + <row> + <entry> <literal>||</literal> </entry> + <entry>concatenation</entry> + <entry><literal>B'10001' || B'011'</literal></entry> + <entry><literal>10001011</literal></entry> + </row> + + <row> + <entry> <literal>&</literal> </entry> + <entry>bitwise AND</entry> + <entry><literal>B'10001' & B'01101'</literal></entry> + <entry><literal>00001</literal></entry> + </row> + + <row> + <entry> <literal>|</literal> </entry> + <entry>bitwise OR</entry> + <entry><literal>B'10001' | B'01101'</literal></entry> + <entry><literal>11101</literal></entry> + </row> + + <row> + <entry> <literal>#</literal> </entry> + <entry>bitwise XOR</entry> + <entry><literal>B'10001' # B'01101'</literal></entry> + <entry><literal>11100</literal></entry> + </row> + + <row> + <entry> <literal>~</literal> </entry> + <entry>bitwise NOT</entry> + <entry><literal>~ B'10001'</literal></entry> + <entry><literal>01110</literal></entry> + </row> + + <row> + <entry> <literal><<</literal> </entry> + <entry>bitwise shift left</entry> + <entry><literal>B'10001' << 3</literal></entry> + <entry><literal>01000</literal></entry> + </row> + + <row> + <entry> <literal>>></literal> </entry> + <entry>bitwise shift right</entry> + <entry><literal>B'10001' >> 2</literal></entry> + <entry><literal>00100</literal></entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + The following <acronym>SQL</acronym>-standard functions work on bit + strings as well as character strings: + <literal><function>length</function></literal>, + <literal><function>bit_length</function></literal>, + <literal><function>octet_length</function></literal>, + <literal><function>position</function></literal>, + <literal><function>substring</function></literal>. + </para> + + <para> + In addition, it is possible to cast integral values to and from type + <type>bit</>. + Some examples: +<programlisting> +44::bit(10) <lineannotation>0000101100</lineannotation> +44::bit(3) <lineannotation>100</lineannotation> +cast(-44 as bit(12)) <lineannotation>111111010100</lineannotation> +'1110'::bit(4)::integer <lineannotation>14</lineannotation> +</programlisting> + Note that casting to just <quote>bit</> means casting to + <literal>bit(1)</>, and so it will deliver only the least significant + bit of the integer. + </para> + + <note> + <para> + Prior to <productname>PostgreSQL</productname> 7.5, casting an + integer to <type>bit(n)</> would copy the leftmost <literal>n</> + bits of the integer, whereas now it copies the rightmost <literal>n</> + bits. Also, casting an integer to a bit string width wider than + the integer itself will sign-extend on the left. + </para> + </note> + + </sect1> + + <sect1 id="functions-matching"> <title>Pattern Matching</title> @@ -7628,14 +7710,13 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <function>bit_and(<replaceable class="parameter">expression</replaceable>)</function> </entry> <entry> - <type>smallint</type>, <type>integer</type>, <type>bigint</type> or - <type>bit</type>, + <type>smallint</type>, <type>integer</type>, <type>bigint</type>, or + <type>bit</type> </entry> <entry> - same as argument data type. - </entry> - <entry>the bitwise-and of all non-null input values, or null if empty + same as argument data type </entry> + <entry>the bitwise AND of all non-null input values, or null if none</entry> </row> <row> @@ -7646,14 +7727,13 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <function>bit_or(<replaceable class="parameter">expression</replaceable>)</function> </entry> <entry> - <type>smallint</type>, <type>integer</type>, <type>bigint</type> or - <type>bit</type>, + <type>smallint</type>, <type>integer</type>, <type>bigint</type>, or + <type>bit</type> </entry> <entry> - same as argument data type. - </entry> - <entry>the bitwise-or of all non-null input values, or null if empty. + same as argument data type </entry> + <entry>the bitwise OR of all non-null input values, or null if none</entry> </row> <row> @@ -7669,9 +7749,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <entry> <type>bool</type> </entry> - <entry>true if all input values are true, otherwise false. - Also known as <function>bool_and</function>. - </entry> + <entry>true if all input values are true, otherwise false</entry> </row> <row> @@ -7720,9 +7798,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <entry> <type>bool</type> </entry> - <entry>true if all input values are true, otherwise false. - Also known as <function>bool_and</function>. - </entry> + <entry>equivalent to <function>bool_and</function></entry> </row> <row> diff --git a/doc/src/sgml/ref/create_cast.sgml b/doc/src/sgml/ref/create_cast.sgml index a6f5e4aa19a9a782a5e476aa36d1e19b107fdc73..31d2473c3508f6a087bb894a2e762891b1ceea23 100644 --- a/doc/src/sgml/ref/create_cast.sgml +++ b/doc/src/sgml/ref/create_cast.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.16 2004/02/15 06:27:37 neilc Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.17 2004/06/16 01:26:40 tgl Exp $ --> <refentry id="SQL-CREATECAST"> <refmeta> @@ -18,7 +18,7 @@ <refsynopsisdiv> <synopsis> CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>) - WITH FUNCTION <replaceable>funcname</replaceable> (<replaceable>argtype</replaceable>) + WITH FUNCTION <replaceable>funcname</replaceable> (<replaceable>argtypes</replaceable>) [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>) @@ -55,9 +55,9 @@ SELECT CAST(42 AS text); <para> By default, a cast can be invoked only by an explicit cast request, that is an explicit <literal>CAST(<replaceable>x</> AS - <replaceable>typename</>)</literal>, - <replaceable>x</><literal>::</><replaceable>typename</>, or - <replaceable>typename</>(<replaceable>x</>) construct. + <replaceable>typename</>)</literal> or + <replaceable>x</><literal>::</><replaceable>typename</> + construct. </para> <para> @@ -141,15 +141,14 @@ SELECT 'The time is ' || CAST(now() AS text); </varlistentry> <varlistentry> - <term><replaceable>funcname</replaceable>(<replaceable>argtype</replaceable>)</term> + <term><replaceable>funcname</replaceable>(<replaceable>argtypes</replaceable>)</term> <listitem> <para> 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 schema search path. The argument type must be - identical to the source type and the result data type must - match the target type of the cast. + up in the schema search path. The function's result data type must + match the target type of the cast. Its arguments are discussed below. </para> </listitem> </varlistentry> @@ -187,6 +186,42 @@ SELECT 'The time is ' || CAST(now() AS text); </varlistentry> </variablelist> + <para> + Cast implementation functions may have one to three arguments. + The first argument type must be identical to the cast's source type. + The second argument, + if present, must be type <type>integer</>; it receives the type + modifier associated with the destination type, or <literal>-1</> + if there is none. The third argument, + if present, must be type <type>boolean</>; it receives <literal>true</> + if the cast is an explicit cast, <literal>false</> otherwise. + (Bizarrely, the SQL spec demands different behaviors for explicit and + implicit casts in some cases. This argument is supplied for functions + that must implement such casts. It is not recommended that you design + your own datatypes so that this matters.) + </para> + + <para> + Ordinarily a cast must have different source and target data types. + However, it is allowed to declare a cast with identical source and + target types if it has a cast implementation function with more than one + argument. This is used to represent type-specific length coercion + functions in the system catalogs. The named function is used to + coerce a value of the type to the type modifier value given by its + second argument. (Since the grammar presently permits only certain + built-in data types to have type modifiers, this feature is of no + use for user-defined target types, but we mention it for completeness.) + </para> + + <para> + When a cast has different source and + target types and a function that takes more than one argument, it + represents converting from one type to another and applying a length + coercion in a single step. When no such entry is available, coercion + to a type that uses a type modifier involves two steps, one to + convert between datatypes and a second to apply the modifier. + </para> + </refsect1> <refsect1 id="sql-createcast-notes"> @@ -207,10 +242,40 @@ SELECT 'The time is ' || CAST(now() AS text); argument of a different type was automatically a cast function. This convention has been abandoned in face of the introduction of schemas and to be able to represent binary compatible casts in the - system catalogs. (The built-in cast functions still follow this naming - scheme, but they have to be shown as casts in the system catalog <literal>pg_cast</> - now.) + system catalogs. The built-in cast functions still follow this naming + scheme, but they have to be shown as casts in the system catalog + <structname>pg_cast</> as well. + </para> + + <para> + While not required, it is recommended that you continue to follow this old + convention of naming cast implementation functions after the target data + type. Many users are used to being able to cast datatypes using a + function-style notation, that is + <replaceable>typename</>(<replaceable>x</>). This notation is in fact + nothing more nor less than a call of the cast implementation function; it + is not specially treated as a cast. If your conversion functions are not + named to support this convention then you will have surprised users. + Since <productname>PostgreSQL</> allows overloading of the same function + name with different argument types, there is no difficulty in having + multiple conversion functions from different types that all use the + target type's name. </para> + + <note> + <para> + There is one small lie in the preceding paragraph: there is still one + case in which <structname>pg_cast</> will be used to resolve the + meaning of an apparent function call. If a + function call <replaceable>name</>(<replaceable>x</>) matches no + actual function, but <replaceable>name</> is the name of a data type + and <structname>pg_cast</> shows a binary-compatible cast to this + type from the type of <replaceable>x</>, then the call will be construed + as an explicit cast. This exception is made so that binary-compatible + casts can be invoked using functional syntax, even though they lack + any function. + </para> + </note> </refsect1> @@ -234,7 +299,8 @@ CREATE CAST (text AS int4) WITH FUNCTION int4(text); <para> The <command>CREATE CAST</command> command conforms to SQL99, except that SQL99 does not make provisions for binary-compatible - types. <literal>AS IMPLICIT</> is a <productname>PostgreSQL</productname> + types or extra arguments to implementation functions. + <literal>AS IMPLICIT</> is a <productname>PostgreSQL</productname> extension, too. </para> </refsect1> diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index 693f6380ef1badf72122f065bc1575d358a9f70c..b1b5aa1324807411cf96ea872e12a15d77bf60ee 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.93 2004/06/07 04:04:47 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.94 2004/06/16 01:26:38 tgl Exp $ --> <chapter id="sql-syntax"> @@ -1319,7 +1319,7 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> <para> When a cast is applied to a value expression of a known type, it represents a run-time type conversion. The cast will succeed only - if a suitable type conversion function is available. Notice that this + if a suitable type conversion operation has been defined. Notice that this is subtly different from the use of casts with constants, as shown in <xref linkend="sql-syntax-constants-generic">. A cast applied to an unadorned string literal represents the initial assignment of a type diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 77e7d1b18557fe423e83dda5d72a2d242573f795..4a05b40eea8d3f933206d41e4426b9e105be2266 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.226 2004/06/06 00:41:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.227 2004/06/16 01:26:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1565,7 +1565,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, node = coerce_to_domain((Node *) prm, prm->paramtype, attr[attnum - 1]->atttypid, - COERCE_IMPLICIT_CAST); + COERCE_IMPLICIT_CAST, false); constraintexprs[attnum - 1] = ExecPrepareExpr((Expr *) node, estate); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 757869a925ae89bb9e7e445ddcc724e227cf792f..7747eb1d7766ed2e3aa34ed3b988b6cccd253c0b 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.47 2004/05/26 04:41:11 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.48 2004/06/16 01:26:42 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -809,6 +809,7 @@ CreateCast(CreateCastStmt *stmt) Oid sourcetypeid; Oid targettypeid; Oid funcid; + int nargs; char castcontext; Relation relation; HeapTuple tuple; @@ -831,11 +832,6 @@ CreateCast(CreateCastStmt *stmt) errmsg("target data type %s does not exist", TypeNameToString(stmt->targettype)))); - if (sourcetypeid == targettypeid) - ereport(ERROR, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("source data type and target data type are the same"))); - /* No shells, no pseudo-types allowed */ if (!get_typisdefined(sourcetypeid)) ereport(ERROR, @@ -885,14 +881,23 @@ CreateCast(CreateCastStmt *stmt) elog(ERROR, "cache lookup failed for function %u", funcid); procstruct = (Form_pg_proc) GETSTRUCT(tuple); - if (procstruct->pronargs != 1) + nargs = procstruct->pronargs; + if (nargs < 1 || nargs > 3) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("cast function must take one argument"))); + errmsg("cast function must take one to three arguments"))); if (procstruct->proargtypes[0] != sourcetypeid) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("argument of cast function must match source data type"))); + if (nargs > 1 && procstruct->proargtypes[1] != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("second argument of cast function must be type integer"))); + if (nargs > 2 && procstruct->proargtypes[2] != BOOLOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("third argument of cast function must be type boolean"))); if (procstruct->prorettype != targettypeid) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -931,6 +936,7 @@ CreateCast(CreateCastStmt *stmt) /* indicates binary coercibility */ funcid = InvalidOid; + nargs = 0; /* * Must be superuser to create binary-compatible casts, since @@ -957,6 +963,15 @@ CreateCast(CreateCastStmt *stmt) errmsg("source and target data types are not physically compatible"))); } + /* + * Allow source and target types to be same only for length coercion + * functions. We assume a multi-arg function does length coercion. + */ + if (sourcetypeid == targettypeid && nargs < 2) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("source data type and target data type are the same"))); + /* convert CoercionContext enum to char value for castcontext */ switch (stmt->context) { diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index a6b48450a49d9b0ed0812b41d9aaae25226c42db..cc0048972bfe73157d1bf87eb490c68d7d768c09 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.68 2004/05/30 23:40:29 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.69 2004/06/16 01:26:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -199,7 +199,8 @@ expand_targetlist(List *tlist, int command_type, new_expr = coerce_to_domain(new_expr, InvalidOid, atttype, - COERCE_IMPLICIT_CAST); + COERCE_IMPLICIT_CAST, + false); } else { diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index a2cd7dccc15b7cc5de196ecf2c28a65f1e4c7c80..b3a6b67f5c399a0c100744b1bfea15d1aa4f1e4a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.132 2004/06/09 19:08:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.133 2004/06/16 01:26:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -915,11 +915,11 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, * Insert coercion functions if needed. Note that a difference in * typmod can only happen if input has typmod but outcoltypmod is -1. * In that case we insert a RelabelType to clearly mark that result's - * typmod is not same as input. + * typmod is not same as input. We never need coerce_type_typmod. */ if (l_colvar->vartype != outcoltype) l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype, - outcoltype, + outcoltype, outcoltypmod, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (l_colvar->vartypmod != outcoltypmod) l_node = (Node *) makeRelabelType((Expr *) l_colvar, @@ -930,7 +930,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, if (r_colvar->vartype != outcoltype) r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype, - outcoltype, + outcoltype, outcoltypmod, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (r_colvar->vartypmod != outcoltypmod) r_node = (Node *) makeRelabelType((Expr *) r_colvar, @@ -1276,7 +1276,7 @@ transformGroupClause(ParseState *pstate, List *grouplist, if (restype == UNKNOWNOID) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, - restype, TEXTOID, + restype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); restype = tle->resdom->restype = TEXTOID; @@ -1528,7 +1528,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, if (restype == UNKNOWNOID && resolveUnknown) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, - restype, TEXTOID, + restype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); restype = tle->resdom->restype = TEXTOID; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index b12d1854aa8236dffa8503a45b7eb507da5a8c68..3878d07d5a189f8be32b41bb0892b06676359e45 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.118 2004/06/06 00:41:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.119 2004/06/16 01:26:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -33,8 +33,13 @@ static Node *coerce_type_typmod(Node *node, - Oid targetTypeId, int32 targetTypMod, - CoercionForm cformat, bool isExplicit); + Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat, bool isExplicit, + bool hideInputCoercion); +static void hide_coercion_node(Node *node); +static Node *build_coercion_expression(Node *node, Oid funcId, + Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat, bool isExplicit); static Node *coerce_record_to_complex(ParseState *pstate, Node *node, Oid targetTypeId, CoercionContext ccontext, @@ -67,22 +72,27 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, CoercionContext ccontext, CoercionForm cformat) { - if (can_coerce_type(1, &exprtype, &targettype, ccontext)) - expr = coerce_type(pstate, expr, exprtype, targettype, - ccontext, cformat); - else - expr = NULL; + Node *result; + + if (!can_coerce_type(1, &exprtype, &targettype, ccontext)) + return NULL; + + result = coerce_type(pstate, expr, exprtype, + targettype, targettypmod, + ccontext, cformat); /* * If the target is a fixed-length type, it may need a length coercion - * as well as a type coercion. + * as well as a type coercion. If we find ourselves adding both, + * force the inner coercion node to implicit display form. */ - if (expr != NULL) - expr = coerce_type_typmod(expr, targettype, targettypmod, - cformat, - (cformat != COERCE_IMPLICIT_CAST)); + result = coerce_type_typmod(result, + targettype, targettypmod, + cformat, + (cformat != COERCE_IMPLICIT_CAST), + (result != expr && !IsA(result, Const))); - return expr; + return result; } @@ -93,10 +103,13 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, * The caller should already have determined that the coercion is possible; * see can_coerce_type. * - * No coercion to a typmod (length) is performed here. The caller must - * call coerce_type_typmod as well, if a typmod constraint is wanted. + * Normally, no coercion to a typmod (length) is performed here. The caller + * must call coerce_type_typmod as well, if a typmod constraint is wanted. * (But if the target type is a domain, it may internally contain a * typmod constraint, which will be applied inside coerce_to_domain.) + * In some cases pg_cast specifies a type coercion function that also + * applies length conversion, and in those cases only, the result will + * already be properly coerced to the specified typmod. * * pstate is only used in the case that we are able to resolve the type of * a previously UNKNOWN Param. It is okay to pass pstate = NULL if the @@ -104,7 +117,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, */ Node * coerce_type(ParseState *pstate, Node *node, - Oid inputTypeId, Oid targetTypeId, + Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat) { Node *result; @@ -178,7 +191,7 @@ coerce_type(ParseState *pstate, Node *node, /* If target is a domain, apply constraints. */ if (targetTyptype == 'd') result = coerce_to_domain(result, InvalidOid, targetTypeId, - cformat); + cformat, false); ReleaseSysCache(targetType); @@ -240,13 +253,14 @@ coerce_type(ParseState *pstate, Node *node, * Generate an expression tree representing run-time * application of the conversion function. If we are dealing * with a domain target type, the conversion function will - * yield the base type. + * yield the base type (and we assume targetTypeMod must be -1). */ Oid baseTypeId = getBaseType(targetTypeId); - result = (Node *) makeFuncExpr(funcId, baseTypeId, - list_make1(node), - cformat); + result = build_coercion_expression(node, funcId, + baseTypeId, targetTypeMod, + cformat, + (cformat != COERCE_IMPLICIT_CAST)); /* * If domain, coerce to the domain type and relabel with @@ -254,7 +268,7 @@ coerce_type(ParseState *pstate, Node *node, */ if (targetTypeId != baseTypeId) result = coerce_to_domain(result, baseTypeId, targetTypeId, - cformat); + cformat, true); } else { @@ -269,7 +283,7 @@ coerce_type(ParseState *pstate, Node *node, * then we won't need a RelabelType node. */ result = coerce_to_domain(node, InvalidOid, targetTypeId, - cformat); + cformat, false); if (result == node) { /* @@ -409,11 +423,13 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, * has not bothered to look this up) * 'typeId': target type to coerce to * 'cformat': coercion format + * 'hideInputCoercion': if true, hide the input coercion under this one. * * If the target type isn't a domain, the given 'arg' is returned as-is. */ Node * -coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) +coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, + CoercionForm cformat, bool hideInputCoercion) { CoerceToDomain *result; int32 typmod; @@ -426,6 +442,10 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) if (baseTypeId == typeId) return arg; + /* Suppress display of nested coercion steps */ + if (hideInputCoercion) + hide_coercion_node(arg); + /* * If the domain applies a typmod to its base type, build the * appropriate coercion step. Mark it implicit for display purposes, @@ -444,7 +464,8 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) if (typmod >= 0) arg = coerce_type_typmod(arg, baseTypeId, typmod, COERCE_IMPLICIT_CAST, - (cformat != COERCE_IMPLICIT_CAST)); + (cformat != COERCE_IMPLICIT_CAST), + false); /* * Now build the domain coercion node. This represents run-time @@ -473,57 +494,142 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat) * The caller must have already ensured that the value is of the correct * type, typically by applying coerce_type. * + * cformat determines the display properties of the generated node (if any), + * while isExplicit may affect semantics. If hideInputCoercion is true + * *and* we generate a node, the input node is forced to IMPLICIT display + * form, so that only the typmod coercion node will be visible when + * displaying the expression. + * * NOTE: this does not need to work on domain types, because any typmod * coercion for a domain is considered to be part of the type coercion * needed to produce the domain value in the first place. So, no getBaseType. */ static Node * coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, - CoercionForm cformat, bool isExplicit) + CoercionForm cformat, bool isExplicit, + bool hideInputCoercion) { Oid funcId; - int nargs; /* * A negative typmod is assumed to mean that no coercion is wanted. + * Also, skip coercion if already done. */ if (targetTypMod < 0 || targetTypMod == exprTypmod(node)) return node; - funcId = find_typmod_coercion_function(targetTypeId, &nargs); + funcId = find_typmod_coercion_function(targetTypeId); if (OidIsValid(funcId)) { - List *args; - Const *cons; + /* Suppress display of nested coercion steps */ + if (hideInputCoercion) + hide_coercion_node(node); + + node = build_coercion_expression(node, funcId, + targetTypeId, targetTypMod, + cformat, isExplicit); + } - /* Pass given value, plus target typmod as an int4 constant */ + return node; +} + +/* + * Mark a coercion node as IMPLICIT so it will never be displayed by + * ruleutils.c. We use this when we generate a nest of coercion nodes + * to implement what is logically one conversion; the inner nodes are + * forced to IMPLICIT_CAST format. This does not change their semantics, + * only display behavior. + * + * It is caller error to call this on something that doesn't have a + * CoercionForm field. + */ +static void +hide_coercion_node(Node *node) +{ + if (IsA(node, FuncExpr)) + ((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST; + else if (IsA(node, RelabelType)) + ((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST; + else if (IsA(node, RowExpr)) + ((RowExpr *) node)->row_format = COERCE_IMPLICIT_CAST; + else if (IsA(node, CoerceToDomain)) + ((CoerceToDomain *) node)->coercionformat = COERCE_IMPLICIT_CAST; + else + elog(ERROR, "unsupported node type: %d", (int) nodeTag(node)); +} + +/* + * build_coercion_expression() + * Construct a function-call expression for applying a pg_cast entry. + * + * This is used for both type-coercion and length-coercion functions, + * since there is no difference in terms of the calling convention. + */ +static Node * +build_coercion_expression(Node *node, Oid funcId, + Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat, bool isExplicit) +{ + HeapTuple tp; + Form_pg_proc procstruct; + int nargs; + List *args; + Const *cons; + + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcId), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for function %u", funcId); + procstruct = (Form_pg_proc) GETSTRUCT(tp); + + /* + * Asserts essentially check that function is a legal coercion function. + * We can't make the seemingly obvious tests on prorettype and + * proargtypes[0], because of various binary-compatibility cases. + */ + /* Assert(targetTypeId == procstruct->prorettype); */ + Assert(!procstruct->proretset); + Assert(!procstruct->proisagg); + nargs = procstruct->pronargs; + Assert(nargs >= 1 && nargs <= 3); + /* Assert(procstruct->proargtypes[0] == exprType(node)); */ + Assert(nargs < 2 || procstruct->proargtypes[1] == INT4OID); + Assert(nargs < 3 || procstruct->proargtypes[2] == BOOLOID); + + ReleaseSysCache(tp); + + args = list_make1(node); + + if (nargs >= 2) + { + /* Pass target typmod as an int4 constant */ cons = makeConst(INT4OID, sizeof(int32), Int32GetDatum(targetTypMod), false, true); - args = list_make2(node, cons); + args = lappend(args, cons); + } - if (nargs == 3) - { - /* Pass it a boolean isExplicit parameter, too */ - cons = makeConst(BOOLOID, - sizeof(bool), - BoolGetDatum(isExplicit), - false, - true); - - args = lappend(args, cons); - } + if (nargs == 3) + { + /* Pass it a boolean isExplicit parameter, too */ + cons = makeConst(BOOLOID, + sizeof(bool), + BoolGetDatum(isExplicit), + false, + true); - node = (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat); + args = lappend(args, cons); } - return node; + return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat); } + /* * coerce_record_to_complex * Coerce a RECORD to a specific composite type. @@ -803,7 +909,7 @@ coerce_to_common_type(ParseState *pstate, Node *node, if (inputTypeId == targetTypeId) return node; /* no work */ if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT)) - node = coerce_type(pstate, node, inputTypeId, targetTypeId, + node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else ereport(ERROR, @@ -1528,8 +1634,8 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, { /* * If there's no pg_cast entry, perhaps we are dealing with a pair - * of array types. If so, and if the element types have a - * suitable cast, use array_type_coerce(). + * of array types. If so, and if the element types have a suitable + * cast, use array_type_coerce() or array_type_length_coerce(). */ Oid targetElemType; Oid sourceElemType; @@ -1541,7 +1647,23 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, if (find_coercion_pathway(targetElemType, sourceElemType, ccontext, &elemfuncid)) { - *funcid = F_ARRAY_TYPE_COERCE; + if (!OidIsValid(elemfuncid)) + { + /* binary-compatible element type conversion */ + *funcid = F_ARRAY_TYPE_COERCE; + } + else + { + /* does the function take a typmod arg? */ + Oid argtypes[FUNC_MAX_ARGS]; + int nargs; + + (void) get_func_signature(elemfuncid, argtypes, &nargs); + if (nargs > 1) + *funcid = F_ARRAY_TYPE_LENGTH_COERCE; + else + *funcid = F_ARRAY_TYPE_COERCE; + } result = true; } } @@ -1554,14 +1676,8 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, /* * find_typmod_coercion_function -- does the given type need length coercion? * - * If the target type possesses a function named for the type - * and having parameter signature (targettype, int4), we assume that - * the type requires coercion to its own length and that the said - * function should be invoked to do that. - * - * Alternatively, the length-coercing function may have the signature - * (targettype, int4, bool). On success, *nargs is set to report which - * signature we found. + * If the target type possesses a pg_cast function from itself to itself, + * it must need length coercion. * * "bpchar" (ie, char(N)) and "numeric" are examples of such types. * @@ -1569,23 +1685,15 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, * function associated directly with the array type, but instead look for * one associated with the element type. If one exists, we report * array_length_coerce() as the coercion function to use. - * - * This mechanism may seem pretty grotty and in need of replacement by - * something in pg_cast, but since typmod is only interesting for datatypes - * that have special handling in the grammar, there's not really much - * percentage in making it any easier to apply such coercions ... */ Oid -find_typmod_coercion_function(Oid typeId, int *nargs) +find_typmod_coercion_function(Oid typeId) { Oid funcid = InvalidOid; bool isArray = false; Type targetType; Form_pg_type typeForm; - char *typname; - Oid typnamespace; - Oid oid_array[FUNC_MAX_ARGS]; - HeapTuple ftup; + HeapTuple tuple; targetType = typeidType(typeId); typeForm = (Form_pg_type) GETSTRUCT(targetType); @@ -1597,79 +1705,30 @@ find_typmod_coercion_function(Oid typeId, int *nargs) { /* Yes, switch our attention to the element type */ typeId = typeForm->typelem; - ReleaseSysCache(targetType); - targetType = typeidType(typeId); - typeForm = (Form_pg_type) GETSTRUCT(targetType); isArray = true; } + ReleaseSysCache(targetType); - /* Function name is same as type internal name, and in same namespace */ - typname = NameStr(typeForm->typname); - typnamespace = typeForm->typnamespace; - - /* First look for parameters (type, int4) */ - MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid)); - oid_array[0] = typeId; - oid_array[1] = INT4OID; - *nargs = 2; - - ftup = SearchSysCache(PROCNAMENSP, - CStringGetDatum(typname), - Int16GetDatum(2), - PointerGetDatum(oid_array), - ObjectIdGetDatum(typnamespace)); - if (HeapTupleIsValid(ftup)) - { - Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup); - - /* Make sure the function's result type is as expected */ - if (pform->prorettype == typeId && !pform->proretset && - !pform->proisagg) - { - /* Okay to use it */ - funcid = HeapTupleGetOid(ftup); - } - ReleaseSysCache(ftup); - } + /* Look in pg_cast */ + tuple = SearchSysCache(CASTSOURCETARGET, + ObjectIdGetDatum(typeId), + ObjectIdGetDatum(typeId), + 0, 0); - if (!OidIsValid(funcid)) + if (HeapTupleIsValid(tuple)) { - /* Didn't find a function, so now try (type, int4, bool) */ - oid_array[2] = BOOLOID; - *nargs = 3; - - ftup = SearchSysCache(PROCNAMENSP, - CStringGetDatum(typname), - Int16GetDatum(3), - PointerGetDatum(oid_array), - ObjectIdGetDatum(typnamespace)); - if (HeapTupleIsValid(ftup)) - { - Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup); + Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); - /* Make sure the function's result type is as expected */ - if (pform->prorettype == typeId && !pform->proretset && - !pform->proisagg) - { - /* Okay to use it */ - funcid = HeapTupleGetOid(ftup); - } - ReleaseSysCache(ftup); - } + funcid = castForm->castfunc; + ReleaseSysCache(tuple); } - ReleaseSysCache(targetType); - /* * Now, if we did find a coercion function for an array element type, - * report array_length_coerce() as the function to use. We know it - * takes three arguments always. + * report array_length_coerce() as the function to use. */ if (isArray && OidIsValid(funcid)) - { funcid = F_ARRAY_LENGTH_COERCE; - *nargs = 3; - } return funcid; } diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 3b4ad7cf8a06ce0636b24ed6c81e478d09550569..6df4547ba2d8932c4ae90c69b0be7b76cfdd6cfe 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.173 2004/06/09 19:08:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.174 2004/06/16 01:26:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1578,6 +1578,9 @@ exprTypmod(Node *expr) * * If coercedTypmod is not NULL, the typmod is stored there if the expression * is a length-coercion function, else -1 is stored there. + * + * Note that a combined type-and-length coercion will be treated as a + * length coercion by this routine. */ bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 781c94f8c2f8d6a8ef91015420432050f50682b3..d5d71b67afdbe320db81c7d87ecab5154c702afb 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.170 2004/05/30 23:40:35 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.171 2004/06/16 01:26:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -150,7 +150,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * these cases, so why duplicate code... */ return coerce_type(pstate, linitial(fargs), - actual_arg_types[0], rettype, + actual_arg_types[0], rettype, -1, COERCION_EXPLICIT, COERCE_EXPLICIT_CALL); } else if (fdresult == FUNCDETAIL_NORMAL) @@ -726,11 +726,12 @@ func_get_detail(List *funcname, { Oid sourceType = argtypes[0]; Node *arg1 = linitial(fargs); + Oid cfuncid; if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) || (find_coercion_pathway(targetType, sourceType, - COERCION_EXPLICIT, funcid) && - *funcid == InvalidOid)) + COERCION_EXPLICIT, &cfuncid) && + cfuncid == InvalidOid)) { /* Yup, it's a type coercion */ *funcid = InvalidOid; @@ -1122,7 +1123,7 @@ make_fn_arguments(ParseState *pstate, lfirst(current_fargs) = coerce_type(pstate, lfirst(current_fargs), actual_arg_types[i], - declared_arg_types[i], + declared_arg_types[i], -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); } diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 768f8788b7ec6a629ea27d8401186808a8e93431..bccc369dc094fa4147543abdb9fbf41f89494110 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.139 2004/06/09 19:08:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.140 2004/06/16 01:26:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -367,7 +367,8 @@ rewriteTargetList(Query *parsetree, Relation target_relation) new_expr = coerce_to_domain(new_expr, InvalidOid, att_tup->atttypid, - COERCE_IMPLICIT_CAST); + COERCE_IMPLICIT_CAST, + false); } } diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 62361932f985c0eee461cfc2e28d88779ca20cd1..e9951d839a3768a07fbf5ae135aedddd4a962de7 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.104 2004/06/08 20:28:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.105 2004/06/16 01:26:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -109,6 +109,11 @@ static void array_insert_slice(int ndim, int *dim, int *lb, int *st, int *endp, char *srcPtr, int typlen, bool typbyval, char typalign); static int array_cmp(FunctionCallInfo fcinfo); +static Datum array_type_length_coerce_internal(ArrayType *src, + int32 desttypmod, + bool isExplicit, + FmgrInfo *fmgr_info); + /*--------------------------------------------------------------------- * array_in : @@ -1174,82 +1179,6 @@ array_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } -/*------------------------------------------------------------------------- - * array_length_coerce : - * Apply the element type's length-coercion routine to each element - * of the given array. - *------------------------------------------------------------------------- - */ -Datum -array_length_coerce(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - bool isExplicit = PG_GETARG_BOOL(2); - FmgrInfo *fmgr_info = fcinfo->flinfo; - typedef struct - { - Oid elemtype; - FmgrInfo coerce_finfo; - } alc_extra; - alc_extra *my_extra; - FunctionCallInfoData locfcinfo; - - /* If no typmod is provided, shortcircuit the whole thing */ - if (len < 0) - PG_RETURN_ARRAYTYPE_P(v); - - /* - * We arrange to look up the element type's coercion function only - * once per series of calls, assuming the element type doesn't change - * underneath us. - */ - my_extra = (alc_extra *) fmgr_info->fn_extra; - if (my_extra == NULL) - { - fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt, - sizeof(alc_extra)); - my_extra = (alc_extra *) fmgr_info->fn_extra; - my_extra->elemtype = InvalidOid; - } - - if (my_extra->elemtype != ARR_ELEMTYPE(v)) - { - Oid funcId; - int nargs; - - funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v), &nargs); - - if (OidIsValid(funcId)) - fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt); - else - my_extra->coerce_finfo.fn_oid = InvalidOid; - my_extra->elemtype = ARR_ELEMTYPE(v); - } - - /* - * If we didn't find a coercion function, return the array unmodified - * (this should not happen in the normal course of things, but might - * happen if this function is called manually). - */ - if (my_extra->coerce_finfo.fn_oid == InvalidOid) - PG_RETURN_ARRAYTYPE_P(v); - - /* - * Use array_map to apply the function to each array element. - * - * Note: we pass isExplicit whether or not the function wants it ... - */ - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &my_extra->coerce_finfo; - locfcinfo.nargs = 3; - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); - locfcinfo.arg[2] = BoolGetDatum(isExplicit); - - return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v)); -} - /*----------------------------------------------------------------------------- * array_dims : * returns the dimensions of the array pointed to by "v", as a "text" @@ -2879,6 +2808,9 @@ array_insert_slice(int ndim, * array_type_coerce -- allow explicit or assignment coercion from * one array type to another. * + * array_type_length_coerce -- the same, for cases where both type and length + * coercion are done by a single function on the element type. + * * Caller should have already verified that the source element type can be * coerced into the target element type. */ @@ -2886,8 +2818,30 @@ Datum array_type_coerce(PG_FUNCTION_ARGS) { ArrayType *src = PG_GETARG_ARRAYTYPE_P(0); - Oid src_elem_type = ARR_ELEMTYPE(src); FmgrInfo *fmgr_info = fcinfo->flinfo; + + return array_type_length_coerce_internal(src, -1, false, fmgr_info); +} + +Datum +array_type_length_coerce(PG_FUNCTION_ARGS) +{ + ArrayType *src = PG_GETARG_ARRAYTYPE_P(0); + int32 desttypmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + FmgrInfo *fmgr_info = fcinfo->flinfo; + + return array_type_length_coerce_internal(src, desttypmod, + isExplicit, fmgr_info); +} + +static Datum +array_type_length_coerce_internal(ArrayType *src, + int32 desttypmod, + bool isExplicit, + FmgrInfo *fmgr_info) +{ + Oid src_elem_type = ARR_ELEMTYPE(src); typedef struct { Oid srctype; @@ -2946,7 +2900,8 @@ array_type_coerce(PG_FUNCTION_ARGS) { /* should never happen, but check anyway */ elog(ERROR, "no conversion function from %s to %s", - format_type_be(src_elem_type), format_type_be(tgt_elem_type)); + format_type_be(src_elem_type), + format_type_be(tgt_elem_type)); } if (OidIsValid(funcId)) fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt); @@ -2962,23 +2917,103 @@ array_type_coerce(PG_FUNCTION_ARGS) */ if (my_extra->coerce_finfo.fn_oid == InvalidOid) { - ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0)); + ArrayType *result; + result = (ArrayType *) DatumGetPointer(datumCopy(PointerGetDatum(src), + false, -1)); ARR_ELEMTYPE(result) = my_extra->desttype; PG_RETURN_ARRAYTYPE_P(result); } /* * Use array_map to apply the function to each array element. + * + * We pass on the desttypmod and isExplicit flags whether or not the + * function wants them. */ MemSet(&locfcinfo, 0, sizeof(locfcinfo)); locfcinfo.flinfo = &my_extra->coerce_finfo; - locfcinfo.nargs = 1; + locfcinfo.nargs = 3; locfcinfo.arg[0] = PointerGetDatum(src); + locfcinfo.arg[1] = Int32GetDatum(desttypmod); + locfcinfo.arg[2] = BoolGetDatum(isExplicit); return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype); } +/* + * array_length_coerce -- apply the element type's length-coercion routine + * to each element of the given array. + */ +Datum +array_length_coerce(PG_FUNCTION_ARGS) +{ + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); + int32 desttypmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + FmgrInfo *fmgr_info = fcinfo->flinfo; + typedef struct + { + Oid elemtype; + FmgrInfo coerce_finfo; + } alc_extra; + alc_extra *my_extra; + FunctionCallInfoData locfcinfo; + + /* If no typmod is provided, shortcircuit the whole thing */ + if (desttypmod < 0) + PG_RETURN_ARRAYTYPE_P(v); + + /* + * We arrange to look up the element type's coercion function only + * once per series of calls, assuming the element type doesn't change + * underneath us. + */ + my_extra = (alc_extra *) fmgr_info->fn_extra; + if (my_extra == NULL) + { + fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt, + sizeof(alc_extra)); + my_extra = (alc_extra *) fmgr_info->fn_extra; + my_extra->elemtype = InvalidOid; + } + + if (my_extra->elemtype != ARR_ELEMTYPE(v)) + { + Oid funcId; + + funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v)); + + if (OidIsValid(funcId)) + fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt); + else + my_extra->coerce_finfo.fn_oid = InvalidOid; + my_extra->elemtype = ARR_ELEMTYPE(v); + } + + /* + * If we didn't find a coercion function, return the array unmodified + * (this should not happen in the normal course of things, but might + * happen if this function is called manually). + */ + if (my_extra->coerce_finfo.fn_oid == InvalidOid) + PG_RETURN_ARRAYTYPE_P(v); + + /* + * Use array_map to apply the function to each array element. + * + * Note: we pass isExplicit whether or not the function wants it ... + */ + MemSet(&locfcinfo, 0, sizeof(locfcinfo)); + locfcinfo.flinfo = &my_extra->coerce_finfo; + locfcinfo.nargs = 3; + locfcinfo.arg[0] = PointerGetDatum(v); + locfcinfo.arg[1] = Int32GetDatum(desttypmod); + locfcinfo.arg[2] = BoolGetDatum(isExplicit); + + return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v)); +} + /* * accumArrayResult - accumulate one (more) Datum for an array result * diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 589db025d29942c85ea39b625f2a5c54500b6593..4caaabd62cdd53c1e250a8238d8dc6e35830a58f 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.171 2004/06/09 19:08:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.172 2004/06/16 01:26:47 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -194,7 +194,6 @@ static void get_oper_expr(OpExpr *expr, deparse_context *context); static void get_func_expr(FuncExpr *expr, deparse_context *context, bool showimplicit); static void get_agg_expr(Aggref *aggref, deparse_context *context); -static Node *strip_type_coercion(Node *expr, Oid resultType); static void get_const_expr(Const *constval, deparse_context *context); static void get_sublink_expr(SubLink *sublink, deparse_context *context); static void get_from_clause(Query *query, deparse_context *context); @@ -2983,22 +2982,13 @@ get_rule_expr(Node *node, deparse_context *context, !showimplicit) { /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, showimplicit, node); + get_rule_expr_paren(arg, context, false, node); } else { - /* - * Strip off any type coercions on the input, so we - * don't print redundancies like - * x::bpchar::character(8). - * - * XXX Are there any cases where this is a bad idea? - */ - arg = strip_type_coercion(arg, relabel->resulttype); - if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); + get_rule_expr_paren(arg, context, false, node); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); appendStringInfo(buf, "::%s", @@ -3206,11 +3196,6 @@ get_rule_expr(Node *node, deparse_context *context, CoerceToDomain *ctest = (CoerceToDomain *) node; Node *arg = (Node *) ctest->arg; - /* - * Any implicit coercion at the top level of the argument - * is presumably due to the domain's own internal typmod - * coercion, so do not force it to be shown. - */ if (ctest->coercionformat == COERCE_IMPLICIT_CAST && !showimplicit) { @@ -3331,7 +3316,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context, if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit) { get_rule_expr_paren((Node *) linitial(expr->args), context, - showimplicit, (Node *) expr); + false, (Node *) expr); return; } @@ -3349,17 +3334,9 @@ get_func_expr(FuncExpr *expr, deparse_context *context, /* Get the typmod if this is a length-coercion function */ (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod); - /* - * Strip off any type coercions on the input, so we don't print - * redundancies like x::bpchar::character(8). - * - * XXX Are there any cases where this is a bad idea? - */ - arg = strip_type_coercion(arg, rettype); - if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, (Node *) expr); + get_rule_expr_paren(arg, context, false, (Node *) expr); if (!PRETTY_PAREN(context)) appendStringInfoChar(buf, ')'); appendStringInfo(buf, "::%s", @@ -3413,46 +3390,6 @@ get_agg_expr(Aggref *aggref, deparse_context *context) } -/* - * strip_type_coercion - * Strip any type coercion at the top of the given expression tree, - * if it is a coercion to the given datatype. - * - * We use this to avoid printing two levels of coercion in situations where - * the expression tree has a length-coercion node atop a type-coercion node. - * - * Note: avoid stripping a length-coercion node, since two successive - * coercions to different lengths aren't a no-op. Also, never strip a - * CoerceToDomain node, even though it might be effectively just RelabelType. - */ -static Node * -strip_type_coercion(Node *expr, Oid resultType) -{ - if (expr == NULL || exprType(expr) != resultType) - return expr; - - if (IsA(expr, RelabelType) && - ((RelabelType *) expr)->resulttypmod == -1) - return (Node *) ((RelabelType *) expr)->arg; - - if (IsA(expr, FuncExpr)) - { - FuncExpr *func = (FuncExpr *) expr; - - if (func->funcformat != COERCE_EXPLICIT_CAST && - func->funcformat != COERCE_IMPLICIT_CAST) - return expr; /* don't absorb into upper coercion */ - - if (exprIsLengthCoercion(expr, NULL)) - return expr; - - return (Node *) linitial(func->args); - } - - return expr; -} - - /* ---------- * get_const_expr * diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index 81da4fc17499a694f8a47320292197162b8cdc0a..7fd7727e421b3091c98ba7d48ce0f88af33cba4e 100644 --- a/src/backend/utils/adt/varbit.c +++ b/src/backend/utils/adt/varbit.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.38 2003/11/29 19:51:59 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.39 2004/06/16 01:26:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1166,32 +1166,58 @@ bitshiftright(PG_FUNCTION_ARGS) PG_RETURN_VARBIT_P(result); } -/* This is not defined in any standard. We retain the natural ordering of +/* + * This is not defined in any standard. We retain the natural ordering of * bits here, as it just seems more intuitive. */ Datum bitfromint4(PG_FUNCTION_ARGS) { int32 a = PG_GETARG_INT32(0); + int32 typmod = PG_GETARG_INT32(1); VarBit *result; bits8 *r; - int len; + int rlen; + int destbitsleft, + srcbitsleft; - /* allocate enough space for the bits in an int4 */ - len = VARBITTOTALLEN(sizeof(int4) * BITS_PER_BYTE); - result = (VarBit *) palloc(len); - VARATT_SIZEP(result) = len; - VARBITLEN(result) = sizeof(int4) * BITS_PER_BYTE; + if (typmod <= 0) + typmod = 1; /* default bit length */ + + rlen = VARBITTOTALLEN(typmod); + result = (VarBit *) palloc(rlen); + VARATT_SIZEP(result) = rlen; + VARBITLEN(result) = typmod; - /* - * masks and shifts here are just too painful and we know that an int4 - * has got 4 bytes - */ r = VARBITS(result); - r[0] = (bits8) ((a >> (3 * BITS_PER_BYTE)) & BITMASK); - r[1] = (bits8) ((a >> (2 * BITS_PER_BYTE)) & BITMASK); - r[2] = (bits8) ((a >> (1 * BITS_PER_BYTE)) & BITMASK); - r[3] = (bits8) (a & BITMASK); + destbitsleft = typmod; + srcbitsleft = 32; + /* drop any input bits that don't fit */ + srcbitsleft = Min(srcbitsleft, destbitsleft); + /* sign-fill any excess bytes in output */ + while (destbitsleft >= srcbitsleft + 8) + { + *r++ = (bits8) ((a < 0) ? BITMASK : 0); + destbitsleft -= 8; + } + /* store first fractional byte */ + if (destbitsleft > srcbitsleft) + { + *r++ = (bits8) ((a >> (srcbitsleft - 8)) & BITMASK); + destbitsleft -= 8; + } + /* Now srcbitsleft and destbitsleft are the same, need not track both */ + /* store whole bytes */ + while (destbitsleft >= 8) + { + *r++ = (bits8) ((a >> (destbitsleft - 8)) & BITMASK); + destbitsleft -= 8; + } + /* store last fractional byte */ + if (destbitsleft > 0) + { + *r = (bits8) ((a << (8 - destbitsleft)) & BITMASK); + } PG_RETURN_VARBIT_P(result); } @@ -1204,7 +1230,7 @@ bittoint4(PG_FUNCTION_ARGS) bits8 *r; /* Check that the bit string is not too long */ - if (VARBITLEN(arg) > sizeof(int4) * BITS_PER_BYTE) + if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); @@ -1224,46 +1250,62 @@ bittoint4(PG_FUNCTION_ARGS) Datum bitfromint8(PG_FUNCTION_ARGS) { -#ifndef INT64_IS_BUSTED int64 a = PG_GETARG_INT64(0); + int32 typmod = PG_GETARG_INT32(1); VarBit *result; bits8 *r; - int len; + int rlen; + int destbitsleft, + srcbitsleft; - /* allocate enough space for the bits in an int64 */ - len = VARBITTOTALLEN(sizeof(a) * BITS_PER_BYTE); - result = (VarBit *) palloc(len); - VARATT_SIZEP(result) = len; - VARBITLEN(result) = sizeof(a) * BITS_PER_BYTE; + if (typmod <= 0) + typmod = 1; /* default bit length */ - /* - * masks and shifts here are just too painful and we know that an - * int64 has got 8 bytes - */ - r = VARBITS(result); - r[0] = (bits8) ((a >> (7 * BITS_PER_BYTE)) & BITMASK); - r[1] = (bits8) ((a >> (6 * BITS_PER_BYTE)) & BITMASK); - r[2] = (bits8) ((a >> (5 * BITS_PER_BYTE)) & BITMASK); - r[3] = (bits8) ((a >> (4 * BITS_PER_BYTE)) & BITMASK); - r[4] = (bits8) ((a >> (3 * BITS_PER_BYTE)) & BITMASK); - r[5] = (bits8) ((a >> (2 * BITS_PER_BYTE)) & BITMASK); - r[6] = (bits8) ((a >> (1 * BITS_PER_BYTE)) & BITMASK); - r[7] = (bits8) (a & BITMASK); + rlen = VARBITTOTALLEN(typmod); + result = (VarBit *) palloc(rlen); + VARATT_SIZEP(result) = rlen; + VARBITLEN(result) = typmod; - PG_RETURN_VARBIT_P(result); + r = VARBITS(result); + destbitsleft = typmod; +#ifndef INT64_IS_BUSTED + srcbitsleft = 64; #else - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("64-bit integers not supported on this platform"))); - - PG_RETURN_NULL(); + srcbitsleft = 32; /* don't try to shift more than 32 */ #endif + /* drop any input bits that don't fit */ + srcbitsleft = Min(srcbitsleft, destbitsleft); + /* sign-fill any excess bytes in output */ + while (destbitsleft >= srcbitsleft + 8) + { + *r++ = (bits8) ((a < 0) ? BITMASK : 0); + destbitsleft -= 8; + } + /* store first fractional byte */ + if (destbitsleft > srcbitsleft) + { + *r++ = (bits8) ((a >> (srcbitsleft - 8)) & BITMASK); + destbitsleft -= 8; + } + /* Now srcbitsleft and destbitsleft are the same, need not track both */ + /* store whole bytes */ + while (destbitsleft >= 8) + { + *r++ = (bits8) ((a >> (destbitsleft - 8)) & BITMASK); + destbitsleft -= 8; + } + /* store last fractional byte */ + if (destbitsleft > 0) + { + *r = (bits8) ((a << (8 - destbitsleft)) & BITMASK); + } + + PG_RETURN_VARBIT_P(result); } Datum bittoint8(PG_FUNCTION_ARGS) { -#ifndef INT64_IS_BUSTED VarBit *arg = PG_GETARG_VARBIT_P(0); uint64 result; bits8 *r; @@ -1284,13 +1326,6 @@ bittoint8(PG_FUNCTION_ARGS) result >>= VARBITPAD(arg); PG_RETURN_INT64(result); -#else - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("64-bit integers not supported on this platform"))); - - PG_RETURN_NULL(); -#endif } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 2bebe32b61501c55e53e6371313a39189cbf482b..6fd25b54f7a16b58c60ce7b4dde2cada442abc73 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.235 2004/06/13 21:57:25 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.236 2004/06/16 01:26:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200406131 +#define CATALOG_VERSION_NO 200406151 #endif diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h index 549fa5f80db9c7ede4f6c761e7a9af1a1aeeae85..069f2e8e60cd8c5553f754cf7b2113fb9c40501f 100644 --- a/src/include/catalog/pg_cast.h +++ b/src/include/catalog/pg_cast.h @@ -4,10 +4,13 @@ * definition of the system "type casts" relation (pg_cast) * along with the relation's initial contents. * + * As of Postgres 7.5, pg_cast describes not only type coercion functions + * but also length coercion functions. + * * * Copyright (c) 2002-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.11 2004/03/15 01:13:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.12 2004/06/16 01:26:49 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -43,7 +46,7 @@ typedef enum CoercionCodes * expression */ COERCION_CODE_ASSIGNMENT = 'a', /* coercion in context of * assignment */ - COERCION_CODE_EXPLICIT = 'e' /* explicit cast operation */ + COERCION_CODE_EXPLICIT = 'e' /* explicit cast operation */ } CoercionCodes; @@ -361,4 +364,18 @@ DATA(insert ( 1042 1266 938 e )); DATA(insert ( 1700 1042 1688 a )); DATA(insert ( 1042 1700 1686 e )); +/* + * Length-coercion functions + */ +DATA(insert ( 1042 1042 668 i )); +DATA(insert ( 1043 1043 669 i )); +DATA(insert ( 1083 1083 1968 i )); +DATA(insert ( 1114 1114 1961 i )); +DATA(insert ( 1184 1184 1967 i )); +DATA(insert ( 1186 1186 1200 i )); +DATA(insert ( 1266 1266 1969 i )); +DATA(insert ( 1560 1560 1685 i )); +DATA(insert ( 1562 1562 1687 i )); +DATA(insert ( 1700 1700 1703 i )); + #endif /* PG_CAST_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index b7fad1fca529e88167ccedbb83a7b4b4078e8cfe..ecaf82efab1743d6b1436060368f82f134394b65 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.337 2004/06/13 21:57:26 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.338 2004/06/16 01:26:49 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1028,8 +1028,8 @@ DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f t f i 2 2277 "2283 DESCR("prepend element onto front of array"); DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" _null_ array_cat - _null_ )); DESCR("concatenate two arrays"); -DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" _null_ array_type_coerce - _null_ )); -DESCR("coerce array type to another array type"); +DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f s 1 2277 "2277" _null_ array_type_coerce - _null_ )); +DESCR("coerce array to another array type"); DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 f f t f i 2 1009 "25 25" _null_ text_to_array - _null_ )); DESCR("split delimited text into text[]"); DATA(insert OID = 395 ( array_to_string PGNSP PGUID 12 f f t f i 2 25 "2277 25" _null_ array_to_text - _null_ )); @@ -1587,8 +1587,8 @@ DESCR("convert int8 to text"); DATA(insert OID = 1290 ( int8 PGNSP PGUID 12 f f t f i 1 20 "25" _null_ text_int8 - _null_ )); DESCR("convert text to int8"); -DATA(insert OID = 1291 ( array_length_coerce PGNSP PGUID 12 f f t f i 3 2277 "2277 23 16" _null_ array_length_coerce - _null_ )); -DESCR("adjust any array to element typmod length"); +DATA(insert OID = 1291 ( array_length_coerce PGNSP PGUID 12 f f t f s 3 2277 "2277 23 16" _null_ array_length_coerce - _null_ )); +DESCR("adjust any array to new element typmod"); DATA(insert OID = 1292 ( tideq PGNSP PGUID 12 f f t f i 2 16 "27 27" _null_ tideq - _null_ )); DESCR("equal"); @@ -1722,6 +1722,9 @@ DESCR("convert time to interval"); DATA(insert OID = 1372 ( char_length PGNSP PGUID 12 f f t f i 1 23 "1042" _null_ bpcharlen - _null_ )); DESCR("character length"); +DATA(insert OID = 1373 ( array_type_length_coerce PGNSP PGUID 12 f f t f s 3 2277 "2277 23 16" _null_ array_type_length_coerce - _null_ )); +DESCR("coerce array to another type and adjust element typmod"); + DATA(insert OID = 1374 ( octet_length PGNSP PGUID 12 f f t f i 1 23 "25" _null_ textoctetlen - _null_ )); DESCR("octet length"); DATA(insert OID = 1375 ( octet_length PGNSP PGUID 12 f f t f i 1 23 "1042" _null_ bpcharoctetlen - _null_ )); @@ -2298,7 +2301,7 @@ DATA(insert OID = 1681 ( length PGNSP PGUID 12 f f t f i 1 23 "1560" _null_ b DESCR("bitstring length"); DATA(insert OID = 1682 ( octet_length PGNSP PGUID 12 f f t f i 1 23 "1560" _null_ bitoctetlength - _null_ )); DESCR("octet length"); -DATA(insert OID = 1683 ( bit PGNSP PGUID 12 f f t f i 1 1560 "23" _null_ bitfromint4 - _null_ )); +DATA(insert OID = 1683 ( bit PGNSP PGUID 12 f f t f i 2 1560 "23 23" _null_ bitfromint4 - _null_ )); DESCR("int4 to bitstring"); DATA(insert OID = 1684 ( int4 PGNSP PGUID 12 f f t f i 1 23 "1560" _null_ bittoint4 - _null_ )); DESCR("bitstring to int4"); @@ -2968,7 +2971,7 @@ DESCR("extracts text matching regular expression"); DATA(insert OID = 2074 ( substring PGNSP PGUID 14 f f t f i 3 25 "25 25 25" _null_ "select pg_catalog.substring($1, pg_catalog.similar_escape($2, $3))" - _null_ )); DESCR("extracts text matching SQL99 regular expression"); -DATA(insert OID = 2075 ( bit PGNSP PGUID 12 f f t f i 1 1560 "20" _null_ bitfromint8 - _null_ )); +DATA(insert OID = 2075 ( bit PGNSP PGUID 12 f f t f i 2 1560 "20 23" _null_ bitfromint8 - _null_ )); DESCR("int8 to bitstring"); DATA(insert OID = 2076 ( int8 PGNSP PGUID 12 f f t f i 1 20 "1560" _null_ bittoint8 - _null_ )); DESCR("bitstring to int8"); diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 8700200b51bf69b05310185284e0b7e478bb1183..c1c83d514d959bd3a61f152b21e89c43da99dc64 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.56 2003/11/29 22:41:09 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.57 2004/06/16 01:26:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,10 +47,10 @@ extern Node *coerce_to_target_type(ParseState *pstate, extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, CoercionContext ccontext); extern Node *coerce_type(ParseState *pstate, Node *node, - Oid inputTypeId, Oid targetTypeId, + Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat); extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, - CoercionForm cformat); + CoercionForm cformat, bool hideInputCoercion); extern Node *coerce_to_boolean(ParseState *pstate, Node *node, const char *constructName); @@ -76,6 +76,6 @@ extern Oid resolve_generic_type(Oid declared_type, extern bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid); -extern Oid find_typmod_coercion_function(Oid typeId, int *nargs); +extern Oid find_typmod_coercion_function(Oid typeId); #endif /* PARSE_COERCE_H */ diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 596cf79d4d3849080e39052436db474889751b1c..3114aefe10b0465a3c82a17a4069d7a2338800c1 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.47 2004/06/06 00:41:28 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.48 2004/06/16 01:26:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -116,7 +116,6 @@ extern Datum array_in(PG_FUNCTION_ARGS); extern Datum array_out(PG_FUNCTION_ARGS); extern Datum array_recv(PG_FUNCTION_ARGS); extern Datum array_send(PG_FUNCTION_ARGS); -extern Datum array_length_coerce(PG_FUNCTION_ARGS); extern Datum array_eq(PG_FUNCTION_ARGS); extern Datum array_ne(PG_FUNCTION_ARGS); extern Datum array_lt(PG_FUNCTION_ARGS); @@ -128,6 +127,8 @@ extern Datum array_dims(PG_FUNCTION_ARGS); extern Datum array_lower(PG_FUNCTION_ARGS); extern Datum array_upper(PG_FUNCTION_ARGS); extern Datum array_type_coerce(PG_FUNCTION_ARGS); +extern Datum array_type_length_coerce(PG_FUNCTION_ARGS); +extern Datum array_length_coerce(PG_FUNCTION_ARGS); extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx, int arraylen, int elmlen, bool elmbyval, char elmalign, diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 95b3a2b2ebb0e14cb711d02a224e4a97a3dcf528..f6cebd9a57a6eaa4c4d96b8c2d4573cd4ab82873 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -225,13 +225,28 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT (1 row) -- **************** pg_cast **************** --- Look for casts from and to the same type. This is not harmful, but --- useless. Also catch bogus values in pg_cast columns (other than --- cases detected by oidjoins test). +-- Catch bogus values in pg_cast columns (other than cases detected by +-- oidjoins test). SELECT * FROM pg_cast c -WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0 - OR castcontext NOT IN ('e', 'a', 'i'); +WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i'); + castsource | casttarget | castfunc | castcontext +------------+------------+----------+------------- +(0 rows) + +-- Look for casts to/from the same type that aren't length coercion functions. +-- (We assume they are length coercions if they take multiple arguments.) +-- Such entries are not necessarily harmful, but they are useless. +SELECT * +FROM pg_cast c +WHERE castsource = casttarget AND castfunc = 0; + castsource | casttarget | castfunc | castcontext +------------+------------+----------+------------- +(0 rows) + +SELECT c.* +FROM pg_cast c, pg_proc p +WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget; castsource | casttarget | castfunc | castcontext ------------+------------+----------+------------- (0 rows) @@ -246,7 +261,7 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0 SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND - (p.pronargs <> 1 + (p.pronargs < 1 OR p.pronargs > 3 OR NOT (binary_coercible(c.castsource, p.proargtypes[0]) OR (c.castsource = 'character'::regtype AND p.proargtypes[0] = 'text'::regtype)) @@ -255,6 +270,15 @@ WHERE c.castfunc = p.oid AND ------------+------------+----------+------------- (0 rows) +SELECT c.* +FROM pg_cast c, pg_proc p +WHERE c.castfunc = p.oid AND + ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR + (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype)); + castsource | casttarget | castfunc | castcontext +------------+------------+----------+------------- +(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 usually not intended. diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 9c17a0cd3e11a409d238ec423813f6c81b827185..78ea8dc0cbf54a68c05079ff44bcac4c96293eac 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -184,14 +184,24 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT -- **************** pg_cast **************** --- Look for casts from and to the same type. This is not harmful, but --- useless. Also catch bogus values in pg_cast columns (other than --- cases detected by oidjoins test). +-- Catch bogus values in pg_cast columns (other than cases detected by +-- oidjoins test). SELECT * FROM pg_cast c -WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0 - OR castcontext NOT IN ('e', 'a', 'i'); +WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i'); + +-- Look for casts to/from the same type that aren't length coercion functions. +-- (We assume they are length coercions if they take multiple arguments.) +-- Such entries are not necessarily harmful, but they are useless. + +SELECT * +FROM pg_cast c +WHERE castsource = casttarget AND castfunc = 0; + +SELECT c.* +FROM pg_cast c, pg_proc p +WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget; -- Look for cast functions that don't have the right signature. The -- argument and result types in pg_proc must be the same as, or binary @@ -204,12 +214,18 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0 SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND - (p.pronargs <> 1 + (p.pronargs < 1 OR p.pronargs > 3 OR NOT (binary_coercible(c.castsource, p.proargtypes[0]) OR (c.castsource = 'character'::regtype AND p.proargtypes[0] = 'text'::regtype)) OR NOT binary_coercible(p.prorettype, c.casttarget)); +SELECT c.* +FROM pg_cast c, pg_proc p +WHERE c.castfunc = p.oid AND + ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR + (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype)); + -- 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 usually not intended.