<!--
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">
<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
</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">
<!--
-$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">
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>
<!--
-$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
-->
</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>
</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>
<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>
<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>
<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>
<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>
-<!-- $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>
<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>)
<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>
</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>
</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">
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>
<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>
<!--
-$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">
<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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
*
*
* 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
Oid sourcetypeid;
Oid targettypeid;
Oid funcid;
+ int nargs;
char castcontext;
Relation relation;
HeapTuple tuple;
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,
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),
/* indicates binary coercibility */
funcid = InvalidOid;
+ nargs = 0;
/*
* Must be superuser to create binary-compatible casts, since
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)
{
* 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 $
*
*-------------------------------------------------------------------------
*/
new_expr = coerce_to_domain(new_expr,
InvalidOid,
atttype,
- COERCE_IMPLICIT_CAST);
+ COERCE_IMPLICIT_CAST,
+ false);
}
else
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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,
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,
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;
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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;
}
* 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
*/
Node *
coerce_type(ParseState *pstate, Node *node,
- Oid inputTypeId, Oid targetTypeId,
+ Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat)
{
Node *result;
/* If target is a domain, apply constraints. */
if (targetTyptype == 'd')
result = coerce_to_domain(result, InvalidOid, targetTypeId,
- cformat);
+ cformat, false);
ReleaseSysCache(targetType);
* 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
*/
if (targetTypeId != baseTypeId)
result = coerce_to_domain(result, baseTypeId, targetTypeId,
- cformat);
+ cformat, true);
}
else
{
* then we won't need a RelabelType node.
*/
result = coerce_to_domain(node, InvalidOid, targetTypeId,
- cformat);
+ cformat, false);
if (result == node)
{
/*
* 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;
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,
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
* 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.
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,
{
/*
* 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;
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;
}
}
/*
* 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.
*
* 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);
{
/* 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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*
* 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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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)
{
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;
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);
}
* 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 $
*
*-------------------------------------------------------------------------
*/
new_expr = coerce_to_domain(new_expr,
InvalidOid,
att_tup->atttypid,
- COERCE_IMPLICIT_CAST);
+ COERCE_IMPLICIT_CAST,
+ false);
}
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 :
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"
* 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.
*/
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;
{
/* 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);
*/
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
*
* astate is working state (NULL on first call)
* 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.
*
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);
!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",
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)
{
if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
{
get_rule_expr_paren((Node *) linitial(expr->args), context,
- showimplicit, (Node *) expr);
+ false, (Node *) expr);
return;
}
/* 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",
}
-/*
- * 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
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
}
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")));
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;
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
}
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200406131
+#define CATALOG_VERSION_NO 200406151
#endif
* 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
* 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;
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 */
* 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
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_ ));
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");
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_ ));
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");
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");
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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,
(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)
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))
------------+------------+----------+-------------
(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.
-- **************** 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
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.