diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index a8fd81e19c948bf285e9ae82d1d5135264c04785..01dfe6ad73b3c8697b30b6a2981a3b525b46d4dd 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 - $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.59 2002/09/03 01:04:40 tgl Exp $ + $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.60 2002/09/18 21:35:20 tgl Exp $ --> <chapter id="catalogs"> @@ -841,9 +841,8 @@ <title>pg_cast</title> <para> - <structname>pg_cast</structname> stores data type conversion paths - defined with <command>CREATE CAST</command> plus the built-in - conversions. + <structname>pg_cast</structname> stores data type conversion paths, + both built-in paths and those defined with <command>CREATE CAST</command>. </para> <table> @@ -879,17 +878,25 @@ <entry><type>oid</type></entry> <entry>pg_proc.oid</entry> <entry> - The OID of the function to use to perform this cast. A 0 is - stored if the data types are binary compatible (that is, no - function is needed to perform the cast). + The OID of the function to use to perform this cast. Zero is + stored if the data types are binary coercible (that is, no + run-time operation is needed to perform the cast). </entry> </row> <row> - <entry>castimplicit</entry> - <entry><type>bool</type></entry> + <entry>castcontext</entry> + <entry><type>char</type></entry> <entry></entry> - <entry>Indication whether this cast can be invoked implicitly</entry> + <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>a</> means implicitly in assignment + to a target column, as well as explicitly. + <literal>i</> means implicitly in expressions, as well as the + other cases. + </entry> </row> </tbody> </tgroup> diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 41ca3c00e53669a2f05ec6aa74782ac2591dc223..28d3fcb7edeb88303ff341300d740ac775a5f06d 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.102 2002/08/23 02:54:18 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.103 2002/09/18 21:35:20 tgl Exp $ --> <chapter id="datatype"> @@ -823,8 +823,19 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> ( <note> <para> - Prior to <productname>PostgreSQL</> 7.2, strings that were too long were silently - truncated, no error was raised. + If one explicitly casts a value to + <type>character(<replaceable>n</>)</type> or <type>character + varying(<replaceable>n</>)</type>, then an overlength value will + be truncated to <replaceable>n</> characters without raising an + error. (This too is required by the SQL standard.) + </para> + </note> + + <note> + <para> + Prior to <productname>PostgreSQL</> 7.2, strings that were too long were + always truncated without raising an error, in either explicit or + implicit casting contexts. </para> </note> @@ -897,12 +908,14 @@ INSERT INTO test2 VALUES ('ok'); INSERT INTO test2 VALUES ('good '); INSERT INTO test2 VALUES ('too long'); <computeroutput>ERROR: value too long for type character varying(5)</computeroutput> +INSERT INTO test2 VALUES ('too long'::varchar(5)); -- explicit truncation SELECT b, char_length(b) FROM test2; <computeroutput> b | char_length -------+------------- ok | 2 good | 5 + too l | 5 </computeroutput> </programlisting> <calloutlist> @@ -932,7 +945,7 @@ SELECT b, char_length(b) FROM test2; </para> <table tocentry="1"> - <title>Specialty Character Type</title> + <title>Specialty Character Types</title> <tgroup cols="3"> <thead> <row> @@ -2832,29 +2845,39 @@ SELECT * FROM test1 WHERE a; <para> Bit strings are strings of 1's and 0's. They can be used to store or visualize bit masks. There are two SQL bit types: - <type>BIT(<replaceable>x</replaceable>)</type> and <type>BIT - VARYING(<replaceable>x</replaceable>)</type>; where - <replaceable>x</replaceable> is a positive integer. + <type>BIT(<replaceable>n</replaceable>)</type> and <type>BIT + VARYING(<replaceable>n</replaceable>)</type>, where + <replaceable>n</replaceable> is a positive integer. </para> <para> <type>BIT</type> type data must match the length - <replaceable>x</replaceable> exactly; it is an error to attempt to - store shorter or longer bit strings. <type>BIT VARYING</type> is + <replaceable>n</replaceable> exactly; it is an error to attempt to + store shorter or longer bit strings. <type>BIT VARYING</type> data is of variable length up to the maximum length - <replaceable>x</replaceable>; longer strings will be rejected. - <type>BIT</type> without length is equivalent to - <literal>BIT(1)</literal>, <type>BIT VARYING</type> without length + <replaceable>n</replaceable>; longer strings will be rejected. + Writing <type>BIT</type> without a length is equivalent to + <literal>BIT(1)</literal>, while <type>BIT VARYING</type> without a length specification means unlimited length. </para> <note> <para> - Prior to <productname>PostgreSQL</> 7.2, <type>BIT</type> type data was - zero-padded on the right. This was changed to comply with the - SQL standard. To implement zero-padded bit strings, a - combination of the concatenation operator and the - <function>substring</function> function can be used. + If one explicitly casts a bit-string value to + <type>BIT(<replaceable>n</>)</type>, it will be truncated or + zero-padded on the right to be exactly <replaceable>n</> bits, + without raising an error. Similarly, + if one explicitly casts a bit-string value to + <type>BIT VARYING(<replaceable>n</>)</type>, it will be truncated + on the right if it is more than <replaceable>n</> bits. + </para> + </note> + + <note> + <para> + Prior to <productname>PostgreSQL</> 7.2, <type>BIT</type> data was + always silently truncated or zero-padded on the right, with or without an + explicit cast. This was changed to comply with the SQL standard. </para> </note> @@ -2874,9 +2897,16 @@ CREATE TABLE test (a BIT(3), b BIT VARYING(5)); INSERT INTO test VALUES (B'101', B'00'); INSERT INTO test VALUES (B'10', B'101'); <computeroutput> -ERROR: bit string length does not match type bit(3) +ERROR: Bit string length 2 does not match type BIT(3) +</computeroutput> +INSERT INTO test VALUES (B'10'::bit(3), B'101'); +SELECT * FROM test; +<computeroutput> + a | b +-----+----- + 101 | 00 + 100 | 101 </computeroutput> -SELECT SUBSTRING(b FROM 1 FOR 2) FROM test; </programlisting> </example> diff --git a/doc/src/sgml/ref/create_cast.sgml b/doc/src/sgml/ref/create_cast.sgml index bc9f71e566ea78e38b3eff460497bacdcc2800d5..e64d696f81afc9ee11d966e627d54dae1e90318f 100644 --- a/doc/src/sgml/ref/create_cast.sgml +++ b/doc/src/sgml/ref/create_cast.sgml @@ -1,4 +1,4 @@ -<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.4 2002/09/15 13:04:16 petere Exp $ --> +<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.5 2002/09/18 21:35:20 tgl Exp $ --> <refentry id="SQL-CREATECAST"> <refmeta> @@ -15,11 +15,11 @@ <synopsis> CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>) WITH FUNCTION <replaceable>funcname</replaceable> (<replaceable>argtype</replaceable>) - [AS ASSIGNMENT] + [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>) WITHOUT FUNCTION - [AS ASSIGNMENT] + [ AS ASSIGNMENT | AS IMPLICIT ] </synopsis> </refsynopsisdiv> @@ -49,20 +49,44 @@ SELECT CAST(42 AS text); </para> <para> - A cast can be marked <literal>AS ASSIGNMENT</>, which means that it - can be invoked implicitly in any context where the conversion it - defines is required. Cast functions not so marked can be invoked - only by explicit <literal>CAST</>, + 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</>) constructs. For - example, supposing that <literal>foo.f1</literal> is a column of + <replaceable>typename</>(<replaceable>x</>) construct. + </para> + + <para> + If the cast is marked <literal>AS ASSIGNMENT</> then it can be invoked + implicitly when assigning to a column of the target data type. + For example, supposing that <literal>foo.f1</literal> is a column of type <type>text</type>, then <programlisting> INSERT INTO foo(f1) VALUES(42); </programlisting> will be allowed if the cast from type <type>integer</type> to type <type>text</type> is marked <literal>AS ASSIGNMENT</>, otherwise - not. (We generally use the term <firstterm>implicit + not. + (We generally use the term <firstterm>assignment + cast</firstterm> to describe this kind of cast.) + </para> + + <para> + If the cast is marked <literal>AS IMPLICIT</> then it can be invoked + implicitly in any context, whether assignment or internally in an + expression. For example, since <literal>||</> takes <type>text</> + arguments, +<programlisting> +SELECT 'The time is ' || now(); +</programlisting> + will be allowed only if the cast from type <type>timestamp</> to + <type>text</type> is marked <literal>AS IMPLICIT</>. Otherwise it + will be necessary to write one of +<programlisting> +SELECT 'The time is ' || CAST(now() AS text); +SELECT 'The time is ' || now()::text; +</programlisting> + (We generally use the term <firstterm>implicit cast</firstterm> to describe this kind of cast.) </para> @@ -74,10 +98,11 @@ INSERT INTO foo(f1) VALUES(42); all because there are multiple possible interpretations. A good rule of thumb is to make a cast implicitly invokable only for information-preserving transformations between types in the same - general type category. For example, <type>int2</type> to - <type>int4</type> casts can reasonably be implicit, but be wary of - marking <type>int4</type> to <type>text</type> or - <type>float8</type> to <type>int4</type> as implicit casts. + general type category. For example, the cast from <type>int2</type> to + <type>int4</type> can reasonably be implicit, but the cast from + <type>float8</type> to <type>int4</type> should probably be + assignment-only. Cross-type-category casts, such as <type>text</> + to <type>int4</>, are best made explicit-only. </para> <para> @@ -138,7 +163,18 @@ INSERT INTO foo(f1) VALUES(42); <listitem> <para> - Indicates that the cast may be invoked implicitly. + Indicates that the cast may be invoked implicitly in assignment + contexts. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>AS IMPLICIT</literal></term> + + <listitem> + <para> + Indicates that the cast may be invoked implicitly in any context. </para> </listitem> </varlistentry> @@ -163,10 +199,10 @@ INSERT INTO foo(f1) VALUES(42); data type, returned that data type, and took one 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 catalogs. The built-in + able to represent binary compatible casts in the catalogs. (The built-in cast functions - still follow this naming scheme, but they have to be declared as - casts explicitly now. + still follow this naming scheme, but they have to be shown as + casts in <literal>pg_cast</> now.) </para> </refsect1> @@ -191,7 +227,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. + types. <literal>AS IMPLICIT</> is a <productname>PostgreSQL</productname> + extension, too. </para> </refsect1> diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index b7f2b4be71b57b181a4deb8baae6b1fdc5db3cf8..f373cc6e25df86c8c80408365a96fd3406acae44 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.158 2002/09/04 07:16:32 momjian Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.159 2002/09/18 21:35:20 tgl Exp $ --> <appendix id="release"> @@ -24,6 +24,8 @@ CDATA means the content is "SGML-free", so you can write without worries about funny characters. --> <literallayout><![CDATA[ +Mixed numeric-and-float expressions are evaluated as float, per SQL spec +Explicit casts to char, varchar, bit, varbit will truncate or pad without error CREATE OR REPLACE VIEW, CREATE OR REPLACE RULE are available No-autocommit mode is available (set autocommit to off) Substantial improvements in functionality for functions returning sets diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml index 0bfc40b1edf16c8c90867c41b488f9d0fbc26cb7..e6ff564be969ace3e8bd0b9e0c3d85661e0342e1 100644 --- a/doc/src/sgml/typeconv.sgml +++ b/doc/src/sgml/typeconv.sgml @@ -804,13 +804,9 @@ If the non-unknown inputs are not all of the same type category, fail. <step performance="required"> <para> -If one or more non-unknown inputs are of a preferred type in that category, -resolve as that type. -</para></step> - -<step performance="required"> -<para> -Otherwise, resolve as the type of the first non-unknown input. +Choose the first non-unknown input type which is a preferred type in +that category or allows all the non-unknown inputs to be implicitly +coerced to it. </para></step> <step performance="required"> @@ -842,15 +838,16 @@ Here, the unknown-type literal <literal>'b'</literal> will be resolved as type t <para> <screen> -tgl=> SELECT 1.2 AS "Double" UNION SELECT 1; - Double --------- - 1 - 1.2 +tgl=> SELECT 1.2 AS "Numeric" UNION SELECT 1; + Numeric +--------- + 1 + 1.2 (2 rows) </screen> -The literal <literal>1.2</> is of type <type>double precision</>, -the preferred type in the numeric category, so that type is used. +The literal <literal>1.2</> is of type <type>numeric</>, +and the integer value <literal>1</> can be cast implicitly to +<type>numeric</>, so that type is used. </para> </example> @@ -858,27 +855,18 @@ the preferred type in the numeric category, so that type is used. <title>Type Conversion in a Transposed Union</title> <para> -Here the output type of the union is forced to match the type of -the first clause in the union: - <screen> -tgl=> SELECT 1 AS "All integers" +tgl=> SELECT 1 AS "Real" tgl-> UNION SELECT CAST('2.2' AS REAL); - All integers --------------- - 1 - 2 + Real +------ + 1 + 2.2 (2 rows) </screen> -</para> -<para> -Since <type>REAL</type> is not a preferred type, the parser sees no reason -to select it over <type>INTEGER</type> (which is what the 1 is), and instead -falls back on the use-the-first-alternative rule. -This example demonstrates that the preferred-type mechanism doesn't encode -as much information as we'd like. Future versions of -<productname>PostgreSQL</productname> may support a more general notion of -type preferences. +Here, since type <type>real</> cannot be implicitly cast to <type>integer</>, +but <type>integer</> can be implicitly cast to <type>real</>, the union +result type is resolved as <type>real</>. </para> </example> diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index f09d1fc1debb902f5db1b6b202f0364091f620d9..6bc15b8060a3694ec20103945ffdd27941bfa9de 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.226 2002/09/14 22:14:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.227 2002/09/18 21:35:20 tgl Exp $ * * * INTERFACE ROUTINES @@ -51,7 +51,6 @@ #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" -#include "parser/parse_target.h" #include "rewrite/rewriteRemove.h" #include "storage/smgr.h" #include "utils/builtins.h" @@ -1705,17 +1704,16 @@ cookDefault(ParseState *pstate, { Oid type_id = exprType(expr); - if (type_id != atttypid) - { - if (CoerceTargetExpr(pstate, expr, type_id, - atttypid, atttypmod, false) == NULL) - elog(ERROR, "Column \"%s\" is of type %s" - " but default expression is of type %s" - "\n\tYou will need to rewrite or cast the expression", - attname, - format_type_be(atttypid), - format_type_be(type_id)); - } + if (coerce_to_target_type(expr, type_id, + atttypid, atttypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST) == NULL) + elog(ERROR, "Column \"%s\" is of type %s" + " but default expression is of type %s" + "\n\tYou will need to rewrite or cast the expression", + attname, + format_type_be(atttypid), + format_type_be(type_id)); } /* diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 7b83cf960d97e0e8cb87612854e21f3bd9121fbf..37794645ee58d4f9d9a15c5831fd59f42cb34335 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.55 2002/09/04 20:31:14 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.56 2002/09/18 21:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -95,7 +95,7 @@ AggregateCreate(const char *aggName, */ if (proc->proisstrict && agginitval == NULL) { - if (!IsBinaryCompatible(aggBaseType, aggTransType)) + if (!IsBinaryCoercible(aggBaseType, aggTransType)) elog(ERROR, "must not omit initval when transfn is strict and transtype is not compatible with input type"); } ReleaseSysCache(tup); diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index ba667a26542525903cb51443ca323a23b2c27fc9..b915a02c310ac4b619fccce47ce87e98b97037d5 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.93 2002/09/04 20:31:14 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.94 2002/09/18 21:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -369,7 +369,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList) format_type_be(rettype)); restype = ((TargetEntry *) lfirst(tlist))->resdom->restype; - if (!IsBinaryCompatible(restype, rettype)) + if (!IsBinaryCoercible(restype, rettype)) elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", format_type_be(rettype), format_type_be(restype)); } @@ -388,7 +388,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList) if (tlistlen == 1) { restype = ((TargetEntry *) lfirst(tlist))->resdom->restype; - if (IsBinaryCompatible(restype, rettype)) + if (IsBinaryCoercible(restype, rettype)) return; } @@ -426,7 +426,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList) tletype = exprType(tle->expr); atttype = attr->atttypid; - if (!IsBinaryCompatible(tletype, atttype)) + if (!IsBinaryCoercible(tletype, atttype)) elog(ERROR, "function declared to return %s returns %s instead of %s at column %d", format_type_be(rettype), format_type_be(tletype), diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index a935dae7e6e7f07f9dc09b6ec9cafbc86821736b..f67538625ac6fa62ce547458fe1face42bb4f396 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.20 2002/09/15 13:04:16 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.21 2002/09/18 21:35:20 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -601,14 +601,11 @@ CreateCast(CreateCastStmt *stmt) Oid sourcetypeid; Oid targettypeid; Oid funcid; - HeapTuple tuple; + char castcontext; Relation relation; - Form_pg_proc procstruct; - - Datum values[Natts_pg_proc]; - char nulls[Natts_pg_proc]; - int i; - + HeapTuple tuple; + Datum values[Natts_pg_cast]; + char nulls[Natts_pg_cast]; ObjectAddress myself, referenced; @@ -648,24 +645,17 @@ CreateCast(CreateCastStmt *stmt) TypeNameToString(stmt->sourcetype), TypeNameToString(stmt->targettype)); - relation = heap_openr(CastRelationName, RowExclusiveLock); - - tuple = SearchSysCache(CASTSOURCETARGET, - ObjectIdGetDatum(sourcetypeid), - ObjectIdGetDatum(targettypeid), - 0, 0); - if (HeapTupleIsValid(tuple)) - elog(ERROR, "cast from data type %s to data type %s already exists", - TypeNameToString(stmt->sourcetype), - TypeNameToString(stmt->targettype)); - if (stmt->func != NULL) { + Form_pg_proc procstruct; + funcid = LookupFuncNameTypeNames(stmt->func->funcname, stmt->func->funcargs, "CreateCast"); - tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup of function %u failed", funcid); @@ -687,18 +677,51 @@ CreateCast(CreateCastStmt *stmt) } else { - /* indicates binary compatibility */ + /* indicates binary coercibility */ funcid = InvalidOid; } + /* convert CoercionContext enum to char value for castcontext */ + switch (stmt->context) + { + case COERCION_IMPLICIT: + castcontext = COERCION_CODE_IMPLICIT; + break; + case COERCION_ASSIGNMENT: + castcontext = COERCION_CODE_ASSIGNMENT; + break; + case COERCION_EXPLICIT: + castcontext = COERCION_CODE_EXPLICIT; + break; + default: + elog(ERROR, "CreateCast: bogus CoercionContext %c", stmt->context); + castcontext = 0; /* keep compiler quiet */ + break; + } + + relation = heap_openr(CastRelationName, RowExclusiveLock); + + /* + * Check for duplicate. This is just to give a friendly error message, + * the unique index would catch it anyway (so no need to sweat about + * race conditions). + */ + tuple = SearchSysCache(CASTSOURCETARGET, + ObjectIdGetDatum(sourcetypeid), + ObjectIdGetDatum(targettypeid), + 0, 0); + if (HeapTupleIsValid(tuple)) + elog(ERROR, "cast from data type %s to data type %s already exists", + TypeNameToString(stmt->sourcetype), + TypeNameToString(stmt->targettype)); + /* ready to go */ values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid); values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid); values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid); - values[Anum_pg_cast_castimplicit - 1] = BoolGetDatum(stmt->implicit); + values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext); - for (i = 0; i < Natts_pg_cast; ++i) - nulls[i] = ' '; + MemSet(nulls, ' ', Natts_pg_cast); tuple = heap_formtuple(RelationGetDescr(relation), values, nulls); @@ -706,6 +729,7 @@ CreateCast(CreateCastStmt *stmt) CatalogUpdateIndexes(relation, tuple); + /* make dependency entries */ myself.classId = RelationGetRelid(relation); myself.objectId = HeapTupleGetOid(tuple); myself.objectSubId = 0; @@ -732,6 +756,7 @@ CreateCast(CreateCastStmt *stmt) } heap_freetuple(tuple); + heap_close(relation, RowExclusiveLock); } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 6f0c8438a894519a9464c177ea21fcbd8bae3f56..9660cb61b83e468d5ddb5dab0d0e183f73fbb5ca 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.87 2002/09/04 20:31:15 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.88 2002/09/18 21:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -335,8 +335,8 @@ FuncIndexArgs(IndexInfo *indexInfo, for (i = 0; i < nargs; i++) { - if (!IsBinaryCompatible(argTypes[i], true_typeids[i])) - func_error("DefineIndex", funcIndex->funcname, nargs, argTypes, + if (!IsBinaryCoercible(argTypes[i], true_typeids[i])) + func_error("DefineIndex", funcIndex->funcname, nargs, true_typeids, "Index function must be binary-compatible with table datatype"); } @@ -464,7 +464,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType, opClassId = HeapTupleGetOid(tuple); opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype; - if (!IsBinaryCompatible(attrType, opInputType)) + if (!IsBinaryCoercible(attrType, opInputType)) elog(ERROR, "operator class \"%s\" does not accept data type %s", NameListToString(attribute->opclass), format_type_be(attrType)); @@ -508,7 +508,7 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId) nexact++; exactOid = opclass->oid; } - else if (IsBinaryCompatible(opclass->opcintype, attrType)) + else if (IsBinaryCoercible(attrType, opclass->opcintype)) { ncompatible++; compatibleOid = opclass->oid; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 1a9239c57b94c7f24aa1f9cc8cca8e868ec71fc9..898ca62a6005495b8c0e438e5f2332fa667762ab 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -46,7 +46,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.86 2002/09/04 20:31:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.87 2002/09/18 21:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -913,7 +913,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) */ Oid inputType = exprType(aggref->target); - if (!IsBinaryCompatible(inputType, aggform->aggtranstype)) + if (!IsBinaryCoercible(inputType, aggform->aggtranstype)) elog(ERROR, "Aggregate %u needs to have compatible input type and transition type", aggref->aggfnoid); } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 2964085e0aa2958a8d8545f3dd7c552d53e68b10..73e240aca7c2dd24af21f7fb8bc75e3ff85643f8 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.211 2002/09/04 20:31:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.212 2002/09/18 21:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -850,6 +850,7 @@ _copyFunc(Func *from) newnode->funcid = from->funcid; newnode->funcresulttype = from->funcresulttype; newnode->funcretset = from->funcretset; + newnode->funcformat = from->funcformat; /* Do not copy the run-time state, if any */ newnode->func_fcache = NULL; @@ -931,6 +932,7 @@ _copyRelabelType(RelabelType *from) Node_Copy(from, newnode, arg); newnode->resulttype = from->resulttype; newnode->resulttypmod = from->resulttypmod; + newnode->relabelformat = from->relabelformat; return newnode; } @@ -2634,7 +2636,7 @@ _copyCreateCastStmt(CreateCastStmt *from) Node_Copy(from, newnode, sourcetype); Node_Copy(from, newnode, targettype); Node_Copy(from, newnode, func); - newnode->implicit = from->implicit; + newnode->context = from->context; return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 7f677c0837c09359fe50fcb09bda91917a46737f..8c5d6a0337ab3ce671043e4887e81438523604e3 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -20,7 +20,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.158 2002/09/02 02:13:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.159 2002/09/18 21:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -217,6 +217,15 @@ _equalFunc(Func *a, Func *b) return false; if (a->funcretset != b->funcretset) return false; + /* + * Special-case COERCE_DONTCARE, so that pathkeys can build coercion + * nodes that are equal() to both explicit and implicit coercions. + */ + if (a->funcformat != b->funcformat && + a->funcformat != COERCE_DONTCARE && + b->funcformat != COERCE_DONTCARE) + return false; + /* Note we do not look at func_fcache; see notes for _equalOper */ return true; @@ -302,6 +311,14 @@ _equalRelabelType(RelabelType *a, RelabelType *b) return false; if (a->resulttypmod != b->resulttypmod) return false; + /* + * Special-case COERCE_DONTCARE, so that pathkeys can build coercion + * nodes that are equal() to both explicit and implicit coercions. + */ + if (a->relabelformat != b->relabelformat && + a->relabelformat != COERCE_DONTCARE && + b->relabelformat != COERCE_DONTCARE) + return false; return true; } @@ -1475,7 +1492,7 @@ _equalCreateCastStmt(CreateCastStmt *a, CreateCastStmt *b) return false; if (!equal(a->func, b->func)) return false; - if (a->implicit != b->implicit) + if (a->context != b->context) return false; return true; diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 8363fccfa6e40fc33b0e0f3aef6e9058ca5feee6..b92011445e3467f40a56c3d2ad74f98ed9bde8ae 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.34 2002/09/04 20:31:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.35 2002/09/18 21:35:21 tgl Exp $ */ #include "postgres.h" @@ -215,13 +215,14 @@ makeAlias(const char *aliasname, List *colnames) * creates a RelabelType node */ RelabelType * -makeRelabelType(Node *arg, Oid rtype, int32 rtypmod) +makeRelabelType(Node *arg, Oid rtype, int32 rtypmod, CoercionForm rformat) { RelabelType *r = makeNode(RelabelType); r->arg = arg; r->resulttype = rtype; r->resulttypmod = rtypmod; + r->relabelformat = rformat; return r; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 8873c4e3d2c998c52e5c700220ec6fd258407670..fde42291691cd8d0acb8270b2cd12f1bafddbe3b 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.173 2002/09/04 20:31:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.174 2002/09/18 21:35:21 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -860,10 +860,11 @@ static void _outFunc(StringInfo str, Func *node) { appendStringInfo(str, - " FUNC :funcid %u :funcresulttype %u :funcretset %s ", + " FUNC :funcid %u :funcresulttype %u :funcretset %s :funcformat %d ", node->funcid, node->funcresulttype, - booltostr(node->funcretset)); + booltostr(node->funcretset), + (int) node->funcformat); } /* @@ -914,9 +915,11 @@ _outRelabelType(StringInfo str, RelabelType *node) { appendStringInfo(str, " RELABELTYPE :arg "); _outNode(str, node->arg); - - appendStringInfo(str, " :resulttype %u :resulttypmod %d ", - node->resulttype, node->resulttypmod); + appendStringInfo(str, + " :resulttype %u :resulttypmod %d :relabelformat %d ", + node->resulttype, + node->resulttypmod, + (int) node->relabelformat); } /* diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 14edd8e3d13eb6a2d04ff6fcf908a68bb13f33b1..a4f0c81fa1eeec7e2e2f90f8467fc50fbd77a505 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.132 2002/09/04 20:31:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.133 2002/09/18 21:35:21 tgl Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -1129,6 +1129,10 @@ _readFunc(void) token = pg_strtok(&length); /* now read it */ local_node->funcretset = strtobool(token); + token = pg_strtok(&length); /* get :funcformat */ + token = pg_strtok(&length); /* now read it */ + local_node->funcformat = (CoercionForm) atoi(token); + local_node->func_fcache = NULL; return local_node; @@ -1335,6 +1339,10 @@ _readRelabelType(void) token = pg_strtok(&length); /* get resulttypmod */ local_node->resulttypmod = atoi(token); + token = pg_strtok(&length); /* eat :relabelformat */ + token = pg_strtok(&length); /* get relabelformat */ + local_node->relabelformat = (CoercionForm) atoi(token); + return local_node; } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 5595e1aec9929897d8ecb83fb9a4be2d82b83204..cfd20e8f488f91b9170c1b712ac49ae643a846e1 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.122 2002/09/04 20:31:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.123 2002/09/18 21:35:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -825,32 +825,15 @@ match_clause_to_indexkey(RelOptInfo *rel, * is whether the operator has a commutator operator that matches * the index's opclass. * - * We try both the straightforward match and matches that rely on - * recognizing binary-compatible datatypes. For example, if we have - * an expression like "oid = 123", the operator will be oideqint4, - * which we need to replace with oideq in order to recognize it as - * matching an oid_ops index on the oid field. A variant case is where - * the expression is like "oid::int4 = 123", where the given operator - * will be int4eq and again we need to intuit that we want to use oideq. - * * Returns the OID of the matching operator, or InvalidOid if no match. - * Note that the returned OID will be different from the one in the given - * expression if we used a binary-compatible substitution. Also note that - * if indexkey_on_left is FALSE (meaning we need to commute), the returned - * OID is *not* commuted; it can be plugged directly into the given clause. + * (Formerly, this routine might return a binary-compatible operator + * rather than the original one, but that kluge is history.) */ Oid indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left) { Oid expr_op = ((Oper *) clause->oper)->opno; - Oid commuted_op, - new_op; - Operator oldoptup; - Form_pg_operator oldopform; - char *opname; - Oid ltype, - rtype, - indexkeytype; + Oid commuted_op; /* Get the commuted operator if necessary */ if (indexkey_on_left) @@ -860,83 +843,10 @@ indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left) if (commuted_op == InvalidOid) return InvalidOid; - /* Done if the (commuted) operator is a member of the index's opclass */ + /* OK if the (commuted) operator is a member of the index's opclass */ if (op_in_opclass(commuted_op, opclass)) return expr_op; - /* - * Maybe the index uses a binary-compatible operator set. - * - * Get the nominal input types of the given operator and the actual type - * (before binary-compatible relabeling) of the index key. - */ - oldoptup = SearchSysCache(OPEROID, - ObjectIdGetDatum(expr_op), - 0, 0, 0); - if (!HeapTupleIsValid(oldoptup)) - return InvalidOid; /* probably can't happen */ - oldopform = (Form_pg_operator) GETSTRUCT(oldoptup); - opname = pstrdup(NameStr(oldopform->oprname)); - ltype = oldopform->oprleft; - rtype = oldopform->oprright; - ReleaseSysCache(oldoptup); - - if (indexkey_on_left) - { - Node *leftop = (Node *) get_leftop(clause); - - if (leftop && IsA(leftop, RelabelType)) - leftop = ((RelabelType *) leftop)->arg; - indexkeytype = exprType(leftop); - } - else - { - Node *rightop = (Node *) get_rightop(clause); - - if (rightop && IsA(rightop, RelabelType)) - rightop = ((RelabelType *) rightop)->arg; - indexkeytype = exprType(rightop); - } - - /* - * Make sure we have different but binary-compatible types. - */ - if (ltype == indexkeytype && rtype == indexkeytype) - return InvalidOid; /* no chance for a different operator */ - if (!IsBinaryCompatible(ltype, indexkeytype)) - return InvalidOid; - if (!IsBinaryCompatible(rtype, indexkeytype)) - return InvalidOid; - - /* - * OK, look for operator of the same name with the indexkey's data - * type. (In theory this might find a non-semantically-comparable - * operator, but in practice that seems pretty unlikely for - * binary-compatible types.) - */ - new_op = compatible_oper_opid(makeList1(makeString(opname)), - indexkeytype, indexkeytype, true); - - if (OidIsValid(new_op)) - { - if (new_op != expr_op) - { - /* - * OK, we found a binary-compatible operator of the same name; - * now does it match the index? - */ - if (indexkey_on_left) - commuted_op = new_op; - else - commuted_op = get_commutator(new_op); - if (commuted_op == InvalidOid) - return InvalidOid; - - if (op_in_opclass(commuted_op, opclass)) - return new_op; - } - } - return InvalidOid; } diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index fc33d5296ad7fdffc317711b0560ec23e11d62d0..350c761165b4ff65505676f03591ac2d232c7f2e 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.40 2002/09/04 20:31:20 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.41 2002/09/18 21:35:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -520,6 +520,7 @@ build_index_pathkeys(Query *root, funcnode->funcid = index->indproc; funcnode->funcresulttype = get_func_rettype(index->indproc); funcnode->funcretset = false; /* can never be a set */ + funcnode->funcformat = COERCE_DONTCARE; /* to match any user expr */ funcnode->func_fcache = NULL; while (*indexkeys != 0) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index da3568bbd7d8deffbe9eba454016825e477a7b0a..9cdbcc2e5e548206392263f54ef63d5a87341902 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.118 2002/09/04 20:31:21 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.119 2002/09/18 21:35:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1027,8 +1027,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index, Expr *clause = (Expr *) lfirst(i); Expr *newclause; List *leftvarnos; - Oid opclass, - newopno; + Oid opclass; if (!is_opclause((Node *) clause) || length(clause->args) != 2) elog(ERROR, "fix_indxqual_sublist: indexqual clause is not binary opclause"); @@ -1061,23 +1060,13 @@ fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index, index, &opclass); - /* - * Substitute the appropriate operator if the expression operator - * is merely binary-compatible with the index. This shouldn't - * fail, since indxpath.c found it before... - */ - newopno = indexable_operator(newclause, opclass, true); - if (newopno == InvalidOid) - elog(ERROR, "fix_indxqual_sublist: failed to find substitute op"); - ((Oper *) newclause->oper)->opno = newopno; - fixed_qual = lappend(fixed_qual, newclause); /* * Finally, check to see if index is lossy for this operator. If * so, add (a copy of) original form of clause to recheck list. */ - if (op_requires_recheck(newopno, opclass)) + if (op_requires_recheck(((Oper *) newclause->oper)->opno, opclass)) recheck_qual = lappend(recheck_qual, copyObject((Node *) clause)); } diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 5b930e2b5e62ed819a6e008778672f890ce94664..95687cb0d0d0d09eed4ce197f0223888e4d2926c 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 - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.56 2002/09/04 20:31:22 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.57 2002/09/18 21:35:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -187,8 +187,9 @@ expand_targetlist(List *tlist, int command_type, false, /* not a set */ false); if (!att_tup->attisdropped) - new_expr = coerce_type_constraints(NULL, new_expr, - atttype, false); + new_expr = coerce_type_constraints(new_expr, + atttype, + COERCE_IMPLICIT_CAST); break; case CMD_UPDATE: /* Insert NULLs for dropped columns */ diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 414e26c54cbeef538205d4ef7e31f1171f8f3b98..914b0eee618cd1d4bbde35d194f8912c80d08e92 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.79 2002/09/11 14:48:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.80 2002/09/18 21:35:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -420,8 +420,7 @@ generate_setop_tlist(List *colTypes, int flag, } else { - expr = coerce_to_common_type(NULL, - expr, + expr = coerce_to_common_type(expr, colType, "UNION/INTERSECT/EXCEPT"); colTypmod = -1; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 97b242b9b6bc46d3d773c918459eb040f3d4d818..663ae22d9429c0990743e8df57f98ff3ebab1b89 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.248 2002/09/04 20:31:22 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.249 2002/09/18 21:35:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2565,24 +2565,20 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt) given_type_id = exprType(expr); expected_type_id = (Oid) lfirsti(paramtypes); - if (given_type_id != expected_type_id) - { - expr = CoerceTargetExpr(pstate, - expr, - given_type_id, - expected_type_id, - -1, - false); - - if (!expr) - elog(ERROR, "Parameter $%d of type %s cannot be coerced into the expected type %s" - "\n\tYou will need to rewrite or cast the expression", - i, - format_type_be(given_type_id), - format_type_be(expected_type_id)); - } + expr = coerce_to_target_type(expr, given_type_id, + expected_type_id, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); + + if (expr == NULL) + elog(ERROR, "Parameter $%d of type %s cannot be coerced into the expected type %s" + "\n\tYou will need to rewrite or cast the expression", + i, + format_type_be(given_type_id), + format_type_be(expected_type_id)); fix_opids(expr); + lfirst(l) = expr; paramtypes = lnext(paramtypes); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d038cdd46e7b7486f8fa460b2f3c37896e5eeffd..43597306d4410a67c520a26a2e02af25e2f579f1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.366 2002/09/05 22:52:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.367 2002/09/18 21:35:21 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -161,8 +161,8 @@ static void doNegateFloat(Value *v); %type <list> createdb_opt_list, copy_opt_list %type <defelt> createdb_opt_item, copy_opt_item -%type <ival> opt_lock, lock_type -%type <boolean> opt_force, opt_or_replace, opt_assignment +%type <ival> opt_lock, lock_type, cast_context +%type <boolean> opt_force, opt_or_replace %type <list> user_list @@ -349,7 +349,7 @@ static void doNegateFloat(Value *v); HANDLER, HAVING, HOUR_P, - ILIKE, IMMEDIATE, IMMUTABLE, IN_P, INCREMENT, + ILIKE, IMMEDIATE, IMMUTABLE, IMPLICIT_P, IN_P, INCREMENT, INDEX, INHERITS, INITIALLY, INNER_P, INOUT, INPUT, INSENSITIVE, INSERT, INSTEAD, INT, INTEGER, INTERSECT, INTERVAL, INTO, INVOKER, IS, ISNULL, ISOLATION, @@ -3230,29 +3230,30 @@ any_operator: *****************************************************************************/ CreateCastStmt: CREATE CAST '(' ConstTypename AS ConstTypename ')' - WITH FUNCTION function_with_argtypes opt_assignment + WITH FUNCTION function_with_argtypes cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); n->sourcetype = $4; n->targettype = $6; n->func = (FuncWithArgs *) $10; - n->implicit = $11; + n->context = (CoercionContext) $11; $$ = (Node *)n; } | CREATE CAST '(' ConstTypename AS ConstTypename ')' - WITHOUT FUNCTION opt_assignment + WITHOUT FUNCTION cast_context { CreateCastStmt *n = makeNode(CreateCastStmt); n->sourcetype = $4; n->targettype = $6; n->func = NULL; - n->implicit = $10; + n->context = (CoercionContext) $10; $$ = (Node *)n; } ; -opt_assignment: AS ASSIGNMENT { $$ = TRUE; } - | /*EMPTY*/ { $$ = FALSE; } +cast_context: AS IMPLICIT_P { $$ = COERCION_IMPLICIT; } + | AS ASSIGNMENT { $$ = COERCION_ASSIGNMENT; } + | /*EMPTY*/ { $$ = COERCION_EXPLICIT; } ; @@ -7061,6 +7062,7 @@ unreserved_keyword: | HOUR_P | IMMEDIATE | IMMUTABLE + | IMPLICIT_P | INCREMENT | INDEX | INHERITS diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 9a3064ad661596bea9b1ce1287679dc4117f6dab..305ed8601812e1b5ad4e619baeccebd9136d1fec 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.126 2002/08/27 04:55:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.127 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -147,6 +147,7 @@ static const ScanKeyword ScanKeywords[] = { {"ilike", ILIKE}, {"immediate", IMMEDIATE}, {"immutable", IMMUTABLE}, + {"implicit", IMPLICIT_P}, {"in", IN_P}, {"increment", INCREMENT}, {"index", INDEX}, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index a46ff5e2fcd76958fdfc600d9768a9351f89a113..245c0ba422bab57987c85830876c8158c9947898 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.97 2002/09/04 20:31:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.98 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -872,20 +872,24 @@ buildMergedJoinVar(JoinType jointype, Var *l_colvar, Var *r_colvar) * typmod is not same as input. */ if (l_colvar->vartype != outcoltype) - l_node = coerce_type(NULL, (Node *) l_colvar, l_colvar->vartype, - outcoltype, outcoltypmod, false); + l_node = coerce_type((Node *) l_colvar, l_colvar->vartype, + outcoltype, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (l_colvar->vartypmod != outcoltypmod) l_node = (Node *) makeRelabelType((Node *) l_colvar, - outcoltype, outcoltypmod); + outcoltype, outcoltypmod, + COERCE_IMPLICIT_CAST); else l_node = (Node *) l_colvar; if (r_colvar->vartype != outcoltype) - r_node = coerce_type(NULL, (Node *) r_colvar, r_colvar->vartype, - outcoltype, outcoltypmod, false); + r_node = coerce_type((Node *) r_colvar, r_colvar->vartype, + outcoltype, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (r_colvar->vartypmod != outcoltypmod) r_node = (Node *) makeRelabelType((Node *) r_colvar, - outcoltype, outcoltypmod); + outcoltype, outcoltypmod, + COERCE_IMPLICIT_CAST); else r_node = (Node *) r_colvar; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index d57e18f2324e500a7f60adcd81e8ba87a85f4af0..c0081133eb3a5116650ffa0421e4ffcabec263d1 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.83 2002/09/04 20:31:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.84 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,28 +23,104 @@ #include "parser/parse_func.h" #include "parser/parse_type.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" +static Node *coerce_type_typmod(Node *node, + Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat); static Oid PreferredType(CATEGORY category, Oid type); static bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, - bool isExplicit, - Oid *funcid); -static Oid find_typmod_coercion_function(Oid typeId); -static Node *build_func_call(Oid funcid, Oid rettype, List *args); + CoercionContext ccontext, + Oid *funcid); +static Node *build_func_call(Oid funcid, Oid rettype, List *args, + CoercionForm fformat); + + +/* + * coerce_to_target_type() + * Convert an expression to a target type and typmod. + * + * This is the general-purpose entry point for arbitrary type coercion + * operations. Direct use of the component operations can_coerce_type, + * coerce_type, and coerce_type_typmod should be restricted to special + * cases (eg, when the conversion is expected to succeed). + * + * Returns the possibly-transformed expression tree, or NULL if the type + * conversion is not possible. (We do this, rather than elog'ing directly, + * so that callers can generate custom error messages indicating context.) + * + * expr - input expression tree (already transformed by transformExpr) + * exprtype - result type of expr + * targettype - desired result type + * targettypmod - desired result typmod + * ccontext, cformat - context indicators to control coercions + */ +Node * +coerce_to_target_type(Node *expr, Oid exprtype, + Oid targettype, int32 targettypmod, + CoercionContext ccontext, + CoercionForm cformat) +{ + if (can_coerce_type(1, &exprtype, &targettype, ccontext)) + expr = coerce_type(expr, exprtype, targettype, + ccontext, cformat); + /* + * String hacks to get transparent conversions for char and varchar: + * if a coercion to text is available, use it for forced coercions to + * char(n) or varchar(n). + * + * This is pretty grotty, but seems easier to maintain than providing + * entries in pg_cast that parallel all the ones for text. + */ + else if (ccontext >= COERCION_ASSIGNMENT && + (targettype == BPCHAROID || targettype == VARCHAROID)) + { + Oid text_id = TEXTOID; + + if (can_coerce_type(1, &exprtype, &text_id, ccontext)) + { + expr = coerce_type(expr, exprtype, text_id, + ccontext, cformat); + /* Need a RelabelType if no typmod coercion is performed */ + if (targettypmod < 0) + expr = (Node *) makeRelabelType(expr, targettype, -1, + cformat); + } + else + expr = NULL; + } + else + expr = NULL; + + /* + * If the target is a fixed-length type, it may need a length coercion + * as well as a type coercion. + */ + if (expr != NULL) + expr = coerce_type_typmod(expr, targettype, targettypmod, cformat); + + return expr; +} /* * coerce_type() - * Convert a function argument to a different type. + * Convert an expression to a different type. * * 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. + * (But if the target type is a domain, it may internally contain a + * typmod constraint, which will be applied inside coerce_type_constraints.) */ Node * -coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, - Oid targetTypeId, int32 atttypmod, bool isExplicit) +coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId, + CoercionContext ccontext, CoercionForm cformat) { Node *result; Oid funcId; @@ -68,7 +144,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, * example, int4's typinput function will reject "1.2", whereas * float-to-int type conversion will round to integer. * - * XXX if the typinput function is not cachable, we really ought to + * XXX if the typinput function is not immutable, we really ought to * postpone evaluation of the function call until runtime. But * there is no way to represent a typinput function call as an * expression tree, because C-string values are not Datums. (XXX @@ -91,28 +167,31 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, con->constvalue)); /* - * If target is a domain, use the typmod it applies to the - * base type. Note that we call stringTypeDatum using the - * domain's pg_type row, though. This works because the - * domain row has the same typinput and typelem as the base - * type --- ugly... + * We pass typmod -1 to the input routine, primarily because + * existing input routines follow implicit-coercion semantics + * for length checks, which is not always what we want here. + * Any length constraint will be applied later by our caller. + * + * Note that we call stringTypeDatum using the domain's pg_type + * row, if it's a domain. This works because the domain row has + * the same typinput and typelem as the base type --- ugly... */ - if (targetTyptype == 'd') - atttypmod = getBaseTypeMod(targetTypeId, atttypmod); - - newcon->constvalue = stringTypeDatum(targetType, val, atttypmod); + newcon->constvalue = stringTypeDatum(targetType, val, -1); pfree(val); } result = (Node *) newcon; - /* - * If target is a domain, apply constraints (except for typmod, - * which we assume the input routine took care of). - */ + /* If target is a domain, apply constraints. */ if (targetTyptype == 'd') - result = coerce_type_constraints(pstate, result, targetTypeId, - false); + { + result = coerce_type_constraints(result, targetTypeId, + cformat); + /* We might now need a RelabelType. */ + if (exprType(result) != targetTypeId) + result = (Node *) makeRelabelType(result, targetTypeId, -1, + cformat); + } ReleaseSysCache(targetType); } @@ -120,9 +199,10 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, targetTypeId == ANYARRAYOID) { /* assume can_coerce_type verified that implicit coercion is okay */ + /* NB: we do NOT want a RelabelType here */ result = node; } - else if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit, + else if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId)) { if (OidIsValid(funcId)) @@ -135,7 +215,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, */ Oid baseTypeId = getBaseType(targetTypeId); - result = build_func_call(funcId, baseTypeId, makeList1(node)); + result = build_func_call(funcId, baseTypeId, makeList1(node), + cformat); /* * If domain, test against domain constraints and relabel with @@ -143,9 +224,10 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, */ if (targetTypeId != baseTypeId) { - result = coerce_type_constraints(pstate, result, - targetTypeId, true); - result = (Node *) makeRelabelType(result, targetTypeId, -1); + result = coerce_type_constraints(result, targetTypeId, + cformat); + result = (Node *) makeRelabelType(result, targetTypeId, -1, + cformat); } /* @@ -179,8 +261,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, * Also, domains may have value restrictions beyond the base type * that must be accounted for. */ - result = coerce_type_constraints(pstate, node, - targetTypeId, true); + result = coerce_type_constraints(node, targetTypeId, + cformat); /* * XXX could we label result with exprTypmod(node) instead of @@ -189,7 +271,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, * typmod, which is likely but not certain (wrong if target is * a domain, in any case). */ - result = (Node *) makeRelabelType(result, targetTypeId, -1); + result = (Node *) makeRelabelType(result, targetTypeId, -1, + cformat); } } else if (typeInheritsFrom(inputTypeId, targetTypeId)) @@ -199,7 +282,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, * except relabel the type. This is binary compatibility for * complex types. */ - result = (Node *) makeRelabelType(node, targetTypeId, -1); + result = (Node *) makeRelabelType(node, targetTypeId, -1, + cformat); } else { @@ -215,15 +299,14 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, /* * can_coerce_type() - * Can input_typeids be coerced to func_typeids? + * Can input_typeids be coerced to target_typeids? * - * We must be told whether this is an implicit or explicit coercion - * (explicit being a CAST construct, explicit function call, etc). - * We will accept a wider set of coercion cases for an explicit coercion. + * We must be told the context (CAST construct, assignment, implicit coercion) + * as this determines the set of available casts. */ bool -can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, - bool isExplicit) +can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, + CoercionContext ccontext) { int i; @@ -231,7 +314,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, for (i = 0; i < nargs; i++) { Oid inputTypeId = input_typeids[i]; - Oid targetTypeId = func_typeids[i]; + Oid targetTypeId = target_typeids[i]; Oid funcId; /* no problem if same type */ @@ -278,7 +361,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, /* * Otherwise reject; this assumes there are no explicit - * coercions to ANYARRAY. If we don't reject then + * coercion paths to ANYARRAY. If we don't reject then * parse_coerce would have to repeat the above test. */ return false; @@ -288,7 +371,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, * If pg_cast shows that we can coerce, accept. This test now * covers both binary-compatible and coercion-function cases. */ - if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit, + if (find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId)) continue; @@ -312,10 +395,12 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, * Create an expression tree to enforce the constraints (if any) * that should be applied by the type. Currently this is only * interesting for domain types. + * + * NOTE: result tree is not guaranteed to show the correct exprType() for + * the domain; it may show the base type. Caller must relabel if needed. */ Node * -coerce_type_constraints(ParseState *pstate, Node *arg, - Oid typeId, bool applyTypmod) +coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat) { char *notNull = NULL; int32 typmod = -1; @@ -356,8 +441,8 @@ coerce_type_constraints(ParseState *pstate, Node *arg, /* * If domain applies a typmod to its base type, do length coercion. */ - if (applyTypmod && typmod >= 0) - arg = coerce_type_typmod(pstate, arg, typeId, typmod); + if (typmod >= 0) + arg = coerce_type_typmod(arg, typeId, typmod, cformat); /* * Only need to add one NOT NULL check regardless of how many domains @@ -380,8 +465,9 @@ coerce_type_constraints(ParseState *pstate, Node *arg, } -/* coerce_type_typmod() - * Force a value to a particular typmod, if meaningful and possible. +/* + * coerce_type_typmod() + * Force a value to a particular typmod, if meaningful and possible. * * This is applied to values that are going to be stored in a relation * (where we have an atttypmod for the column) as well as values being @@ -394,33 +480,65 @@ coerce_type_constraints(ParseState *pstate, Node *arg, * 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. */ -Node * -coerce_type_typmod(ParseState *pstate, Node *node, - Oid targetTypeId, int32 atttypmod) +static Node * +coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, + CoercionForm cformat) { Oid funcId; + int nargs; /* * A negative typmod is assumed to mean that no coercion is wanted. */ - if (atttypmod < 0 || atttypmod == exprTypmod(node)) + if (targetTypMod < 0 || targetTypMod == exprTypmod(node)) return node; - funcId = find_typmod_coercion_function(targetTypeId); + funcId = find_typmod_coercion_function(targetTypeId, &nargs); if (OidIsValid(funcId)) { + List *args; Const *cons; + Node *fcall; + /* Pass given value, plus target typmod as an int4 constant */ cons = makeConst(INT4OID, sizeof(int32), - Int32GetDatum(atttypmod), + Int32GetDatum(targetTypMod), false, true, false, false); - node = build_func_call(funcId, targetTypeId, makeList2(node, cons)); + args = makeList2(node, cons); + + if (nargs == 3) + { + /* Pass it a boolean isExplicit parameter, too */ + cons = makeConst(BOOLOID, + sizeof(bool), + BoolGetDatum(cformat != COERCE_IMPLICIT_CAST), + false, + true, + false, + false); + + args = lappend(args, cons); + } + + fcall = build_func_call(funcId, targetTypeId, args, cformat); + + /* + * If the input is a constant, apply the length coercion + * function now instead of delaying to runtime. + * + * See the comments for the similar case in coerce_type. + */ + if (node && IsA(node, Const) && + !((Const *) node)->constisnull) + node = eval_const_expressions(fcall); + else + node = fcall; } return node; @@ -437,19 +555,19 @@ Node * coerce_to_boolean(Node *node, const char *constructName) { Oid inputTypeId = exprType(node); - Oid targetTypeId; if (inputTypeId != BOOLOID) { - targetTypeId = BOOLOID; - if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false)) + node = coerce_to_target_type(node, inputTypeId, + BOOLOID, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); + if (node == NULL) { /* translator: first %s is name of a SQL construct, eg WHERE */ elog(ERROR, "Argument of %s must be type boolean, not type %s", constructName, format_type_be(inputTypeId)); } - node = coerce_type(NULL, node, inputTypeId, targetTypeId, -1, - false); } if (expression_returns_set(node)) @@ -472,12 +590,6 @@ coerce_to_boolean(Node *node, const char *constructName) * in the list will be preferred if there is doubt. * 'context' is a phrase to use in the error message if we fail to select * a usable type. - * - * XXX this code is WRONG, since (for example) given the input (int4,int8) - * it will select int4, whereas according to SQL92 clause 9.3 the correct - * answer is clearly int8. To fix this we need a notion of a promotion - * hierarchy within type categories --- something more complete than - * just a single preferred type. */ Oid select_common_type(List *typeids, const char *context) @@ -511,12 +623,13 @@ select_common_type(List *typeids, const char *context) elog(ERROR, "%s types '%s' and '%s' not matched", context, format_type_be(ptype), format_type_be(ntype)); } - else if (IsPreferredType(pcategory, ntype) - && !IsPreferredType(pcategory, ptype) - && can_coerce_type(1, &ptype, &ntype, false)) + else if (!IsPreferredType(pcategory, ptype) && + can_coerce_type(1, &ptype, &ntype, COERCION_IMPLICIT) && + !can_coerce_type(1, &ntype, &ptype, COERCION_IMPLICIT)) { /* - * new one is preferred and can convert? then take it... + * take new type if can coerce to it implicitly but not the + * other way; but if we have a preferred type, stay on it. */ ptype = ntype; pcategory = TypeCategory(ptype); @@ -547,26 +660,20 @@ select_common_type(List *typeids, const char *context) * This is used following select_common_type() to coerce the individual * expressions to the desired type. 'context' is a phrase to use in the * error message if we fail to coerce. - * - * NOTE: pstate may be NULL. */ Node * -coerce_to_common_type(ParseState *pstate, Node *node, - Oid targetTypeId, - const char *context) +coerce_to_common_type(Node *node, Oid targetTypeId, const char *context) { Oid inputTypeId = exprType(node); if (inputTypeId == targetTypeId) return node; /* no work */ - if (can_coerce_type(1, &inputTypeId, &targetTypeId, false)) - node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1, - false); + if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT)) + node = coerce_type(node, inputTypeId, targetTypeId, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else - { elog(ERROR, "%s unable to convert to type %s", context, format_type_be(targetTypeId)); - } return node; } @@ -708,8 +815,6 @@ PreferredType(CATEGORY category, Oid type) type == REGCLASSOID || type == REGTYPEOID) result = OIDOID; - else if (type == NUMERICOID) - result = NUMERICOID; else result = FLOAT8OID; break; @@ -742,49 +847,52 @@ PreferredType(CATEGORY category, Oid type) } /* PreferredType() */ -/* IsBinaryCompatible() - * Check if two types are binary-compatible. +/* IsBinaryCoercible() + * Check if srctype is binary-coercible to targettype. * * This notion allows us to cheat and directly exchange values without * going through the trouble of calling a conversion function. * - * As of 7.3, binary compatibility isn't hardwired into the code anymore. - * We consider two types binary-compatible if there is an implicit, - * no-function-needed pg_cast entry. NOTE that we assume that such - * entries are symmetric, ie, it doesn't matter which type we consider - * source and which target. (cf. checks in opr_sanity regression test) + * As of 7.3, binary coercibility isn't hardwired into the code anymore. + * We consider two types binary-coercible if there is an implicitly + * invokable, no-function-needed pg_cast entry. + * + * This function replaces IsBinaryCompatible(), which was an inherently + * symmetric test. Since the pg_cast entries aren't necessarily symmetric, + * the order of the operands is now significant. */ bool -IsBinaryCompatible(Oid type1, Oid type2) +IsBinaryCoercible(Oid srctype, Oid targettype) { HeapTuple tuple; Form_pg_cast castForm; bool result; /* Fast path if same type */ - if (type1 == type2) + if (srctype == targettype) return true; /* Perhaps the types are domains; if so, look at their base types */ - if (OidIsValid(type1)) - type1 = getBaseType(type1); - if (OidIsValid(type2)) - type2 = getBaseType(type2); + if (OidIsValid(srctype)) + srctype = getBaseType(srctype); + if (OidIsValid(targettype)) + targettype = getBaseType(targettype); /* Somewhat-fast path if same base type */ - if (type1 == type2) + if (srctype == targettype) return true; /* Else look in pg_cast */ tuple = SearchSysCache(CASTSOURCETARGET, - ObjectIdGetDatum(type1), - ObjectIdGetDatum(type2), + ObjectIdGetDatum(srctype), + ObjectIdGetDatum(targettype), 0, 0); if (!HeapTupleIsValid(tuple)) return false; /* no cast */ castForm = (Form_pg_cast) GETSTRUCT(tuple); - result = (castForm->castfunc == InvalidOid) && castForm->castimplicit; + result = (castForm->castfunc == InvalidOid && + castForm->castcontext == COERCION_CODE_IMPLICIT); ReleaseSysCache(tuple); @@ -796,12 +904,15 @@ IsBinaryCompatible(Oid type1, Oid type2) * find_coercion_pathway * Look for a coercion pathway between two types. * - * If we find a matching entry in pg_cast, return TRUE, and set *funcid + * ccontext determines the set of available casts. + * + * If we find a suitable entry in pg_cast, return TRUE, and set *funcid * to the castfunc value (which may be InvalidOid for a binary-compatible * coercion). */ static bool -find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit, +find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, + CoercionContext ccontext, Oid *funcid) { bool result = false; @@ -828,8 +939,29 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit, if (HeapTupleIsValid(tuple)) { Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); + CoercionContext castcontext; + + /* convert char value for castcontext to CoercionContext enum */ + switch (castForm->castcontext) + { + case COERCION_CODE_IMPLICIT: + castcontext = COERCION_IMPLICIT; + break; + case COERCION_CODE_ASSIGNMENT: + castcontext = COERCION_ASSIGNMENT; + break; + case COERCION_CODE_EXPLICIT: + castcontext = COERCION_EXPLICIT; + break; + default: + elog(ERROR, "find_coercion_pathway: bogus castcontext %c", + castForm->castcontext); + castcontext = 0; /* keep compiler quiet */ + break; + } - if (isExplicit || castForm->castimplicit) + /* Rely on ordering of enum for correct behavior here */ + if (ccontext >= castcontext) { *funcid = castForm->castfunc; result = true; @@ -850,30 +982,59 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit, * 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. + * * "bpchar" (ie, char(N)) and "numeric" are examples of such types. * + * If the given type is a varlena array type, we do not look for a coercion + * 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 ... */ -static Oid -find_typmod_coercion_function(Oid typeId) +Oid +find_typmod_coercion_function(Oid typeId, int *nargs) { Oid funcid = InvalidOid; + bool isArray = false; Type targetType; + Form_pg_type typeForm; char *typname; Oid typnamespace; Oid oid_array[FUNC_MAX_ARGS]; HeapTuple ftup; targetType = typeidType(typeId); - typname = NameStr(((Form_pg_type) GETSTRUCT(targetType))->typname); - typnamespace = ((Form_pg_type) GETSTRUCT(targetType))->typnamespace; + typeForm = (Form_pg_type) GETSTRUCT(targetType); + /* Check for a varlena array type (and not a domain) */ + if (typeForm->typelem != InvalidOid && + typeForm->typlen == -1 && + typeForm->typtype != 'd') + { + /* Yes, switch our attention to the element type */ + typeId = typeForm->typelem; + ReleaseSysCache(targetType); + targetType = typeidType(typeId); + typeForm = (Form_pg_type) GETSTRUCT(targetType); + isArray = true; + } + + /* 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), @@ -894,8 +1055,45 @@ find_typmod_coercion_function(Oid typeId) ReleaseSysCache(ftup); } + if (!OidIsValid(funcid)) + { + /* 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); + + /* 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); + } + } + 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. + */ + if (isArray && OidIsValid(funcid)) + { + funcid = F_ARRAY_LENGTH_COERCE; + *nargs = 3; + } + return funcid; } @@ -905,7 +1103,7 @@ find_typmod_coercion_function(Oid typeId) * The argument expressions must have been transformed already. */ static Node * -build_func_call(Oid funcid, Oid rettype, List *args) +build_func_call(Oid funcid, Oid rettype, List *args, CoercionForm fformat) { Func *funcnode; Expr *expr; @@ -914,6 +1112,7 @@ build_func_call(Oid funcid, Oid rettype, List *args) funcnode->funcid = funcid; funcnode->funcresulttype = rettype; funcnode->funcretset = false; /* only possible case here */ + funcnode->funcformat = fformat; funcnode->func_fcache = NULL; expr = makeNode(Expr); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 7be413f6b5bd2b1f34289a1c4c7a6000e11a0295..3873fd37f0de436dd1669b0a7602f9c862724480 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.128 2002/09/04 20:31:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.129 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,7 +28,6 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" -#include "parser/parse_target.h" #include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -40,9 +39,7 @@ static int expr_depth_counter = 0; bool Transform_null_equals = false; -static Node *parser_typecast_constant(Value *expr, TypeName *typename); -static Node *parser_typecast_expression(ParseState *pstate, - Node *expr, TypeName *typename); +static Node *typecast_expression(Node *expr, TypeName *typename); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); @@ -145,10 +142,9 @@ transformExpr(ParseState *pstate, Node *expr) A_Const *con = (A_Const *) expr; Value *val = &con->val; + result = (Node *) make_const(val); if (con->typename != NULL) - result = parser_typecast_constant(val, con->typename); - else - result = (Node *) make_const(val); + result = typecast_expression(result, con->typename); break; } case T_ExprFieldSelect: @@ -175,7 +171,7 @@ transformExpr(ParseState *pstate, Node *expr) TypeCast *tc = (TypeCast *) expr; Node *arg = transformExpr(pstate, tc->arg); - result = parser_typecast_expression(pstate, arg, tc->typename); + result = typecast_expression(arg, tc->typename); break; } case T_A_Expr: @@ -562,8 +558,7 @@ transformExpr(ParseState *pstate, Node *expr) newc->casetype = ptype; /* Convert default result clause, if necessary */ - newc->defresult = coerce_to_common_type(pstate, - newc->defresult, + newc->defresult = coerce_to_common_type(newc->defresult, ptype, "CASE/ELSE"); @@ -572,8 +567,7 @@ transformExpr(ParseState *pstate, Node *expr) { CaseWhen *w = (CaseWhen *) lfirst(args); - w->result = coerce_to_common_type(pstate, - w->result, + w->result = coerce_to_common_type(w->result, ptype, "CASE/WHEN"); } @@ -671,8 +665,12 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) if (indirection == NIL) return basenode; return (Node *) transformArraySubscripts(pstate, - basenode, exprType(basenode), - indirection, false, NULL); + basenode, + exprType(basenode), + exprTypmod(basenode), + indirection, + false, + NULL); } static Node * @@ -1037,23 +1035,13 @@ 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. - * - * We assume that a two-argument function named for a datatype, whose - * output and first argument types are that datatype, and whose second - * input is an int32 constant, represents a forced length coercion. - * - * XXX It'd be better if the parsetree retained some explicit indication - * of the coercion, so we didn't need these heuristics. */ bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) { Func *func; + int nargs; Const *second_arg; - HeapTuple procTuple; - HeapTuple typeTuple; - Form_pg_proc procStruct; - Form_pg_type typeStruct; if (coercedTypmod != NULL) *coercedTypmod = -1; /* default result on failure */ @@ -1067,62 +1055,26 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) Assert(IsA(func, Func)); /* - * If it's not a two-argument function with the second argument being - * an int4 constant, it can't have been created from a length - * coercion. + * If it didn't come from a coercion context, reject. */ - if (length(((Expr *) expr)->args) != 2) - return false; - second_arg = (Const *) lsecond(((Expr *) expr)->args); - if (!IsA(second_arg, Const) || - second_arg->consttype != INT4OID || - second_arg->constisnull) + if (func->funcformat != COERCE_EXPLICIT_CAST && + func->funcformat != COERCE_IMPLICIT_CAST) return false; /* - * Lookup the function in pg_proc - */ - procTuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(func->funcid), - 0, 0, 0); - if (!HeapTupleIsValid(procTuple)) - elog(ERROR, "cache lookup for proc %u failed", func->funcid); - procStruct = (Form_pg_proc) GETSTRUCT(procTuple); - - /* - * It must be a function with two arguments where the first is of the - * same type as the return value and the second is an int4. Also, just - * to be sure, check return type agrees with expr node. + * If it's not a two-argument or three-argument function with the second + * argument being an int4 constant, it can't have been created from a + * length coercion (it must be a type coercion, instead). */ - if (procStruct->pronargs != 2 || - procStruct->prorettype != procStruct->proargtypes[0] || - procStruct->proargtypes[1] != INT4OID || - procStruct->prorettype != ((Expr *) expr)->typeOid) - { - ReleaseSysCache(procTuple); + nargs = length(((Expr *) expr)->args); + if (nargs < 2 || nargs > 3) return false; - } - /* - * Furthermore, the name and namespace of the function must be the - * same as its result type's name/namespace (cf. - * find_coercion_function). - */ - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(procStruct->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "cache lookup for type %u failed", - procStruct->prorettype); - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - if (strcmp(NameStr(procStruct->proname), - NameStr(typeStruct->typname)) != 0 || - procStruct->pronamespace != typeStruct->typnamespace) - { - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); + second_arg = (Const *) lsecond(((Expr *) expr)->args); + if (!IsA(second_arg, Const) || + second_arg->consttype != INT4OID || + second_arg->constisnull) return false; - } /* * OK, it is indeed a length-coercion function. @@ -1130,79 +1082,17 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) if (coercedTypmod != NULL) *coercedTypmod = DatumGetInt32(second_arg->constvalue); - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); return true; } /* - * Produce an appropriate Const node from a constant value produced - * by the parser and an explicit type name to cast to. - */ -static Node * -parser_typecast_constant(Value *expr, TypeName *typename) -{ - Type tp; - Datum datum; - Const *con; - char *const_string = NULL; - bool string_palloced = false; - bool isNull = false; - - tp = typenameType(typename); - - switch (nodeTag(expr)) - { - case T_Integer: - const_string = DatumGetCString(DirectFunctionCall1(int4out, - Int32GetDatum(expr->val.ival))); - string_palloced = true; - break; - case T_Float: - case T_String: - case T_BitString: - const_string = expr->val.str; - break; - case T_Null: - isNull = true; - break; - default: - elog(ERROR, "Cannot cast this expression to type '%s'", - typeTypeName(tp)); - } - - if (isNull) - datum = (Datum) NULL; - else - datum = stringTypeDatum(tp, const_string, typename->typmod); - - con = makeConst(typeTypeId(tp), - typeLen(tp), - datum, - isNull, - typeByVal(tp), - false, /* not a set */ - true /* is cast */ ); - - if (string_palloced) - pfree(const_string); - - ReleaseSysCache(tp); - - return (Node *) con; -} - -/* - * Handle an explicit CAST applied to a non-constant expression. - * (Actually, this works for constants too, but gram.y won't generate - * a TypeCast node if the argument is just a constant.) + * Handle an explicit CAST construct. * * The given expr has already been transformed, but we need to lookup * the type name and then apply any necessary coercion function(s). */ static Node * -parser_typecast_expression(ParseState *pstate, - Node *expr, TypeName *typename) +typecast_expression(Node *expr, TypeName *typename) { Oid inputType = exprType(expr); Oid targetType; @@ -1212,23 +1102,14 @@ parser_typecast_expression(ParseState *pstate, if (inputType == InvalidOid) return expr; /* do nothing if NULL input */ - if (inputType != targetType) - { - expr = CoerceTargetExpr(pstate, expr, inputType, - targetType, typename->typmod, - true); /* explicit coercion */ - if (expr == NULL) - elog(ERROR, "Cannot cast type '%s' to '%s'", - format_type_be(inputType), - format_type_be(targetType)); - } - - /* - * If the target is a fixed-length type, it may need a length coercion - * as well as a type coercion. - */ - expr = coerce_type_typmod(pstate, expr, - targetType, typename->typmod); + expr = coerce_to_target_type(expr, inputType, + targetType, typename->typmod, + COERCION_EXPLICIT, + COERCE_EXPLICIT_CAST); + if (expr == NULL) + elog(ERROR, "Cannot cast type %s to %s", + format_type_be(inputType), + format_type_be(targetType)); return expr; } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 648ddfbaf0ca4391176ad0e4dab1e7c89ffc42b7..9a54a900aeaf4db2c52fd3734e79f786d6d4496f 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.136 2002/09/04 20:31:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.137 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,15 +32,12 @@ #include "utils/syscache.h" -static Node *ParseComplexProjection(ParseState *pstate, - char *funcname, - Node *first_arg); +static Node *ParseComplexProjection(char *funcname, Node *first_arg); static Oid **argtype_inherit(int nargs, Oid *argtypes); static int find_inheritors(Oid relid, Oid **supervec); static Oid **gen_cross_product(InhPaths *arginh, int nargs); -static void make_arguments(ParseState *pstate, - int nargs, +static void make_arguments(int nargs, List *fargs, Oid *input_typeids, Oid *function_typeids); @@ -137,7 +134,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * ParseComplexProjection can't handle the projection, we have * to keep going. */ - retval = ParseComplexProjection(pstate, cname, first_arg); + retval = ParseComplexProjection(cname, first_arg); if (retval) return retval; } @@ -243,8 +240,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * We can do it as a trivial coercion. coerce_type can handle * these cases, so why duplicate code... */ - return coerce_type(pstate, lfirst(fargs), - oid_array[0], rettype, -1, true); + return coerce_type(lfirst(fargs), oid_array[0], rettype, + COERCION_EXPLICIT, COERCE_EXPLICIT_CALL); } else if (fdresult == FUNCDETAIL_NORMAL) { @@ -296,7 +293,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, } /* perform the necessary typecasting of arguments */ - make_arguments(pstate, nargs, fargs, oid_array, true_oid_array); + make_arguments(nargs, fargs, oid_array, true_oid_array); /* build the appropriate output structure */ if (fdresult == FUNCDETAIL_NORMAL) @@ -307,6 +304,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, funcnode->funcid = funcid; funcnode->funcresulttype = rettype; funcnode->funcretset = retset; + funcnode->funcformat = COERCE_EXPLICIT_CALL; funcnode->func_fcache = NULL; expr->typeOid = rettype; @@ -367,7 +365,7 @@ match_argtypes(int nargs, { next_candidate = current_candidate->next; if (can_coerce_type(nargs, input_typeids, current_candidate->args, - false)) + COERCION_IMPLICIT)) { current_candidate->next = *candidates; *candidates = current_candidate; @@ -470,7 +468,7 @@ func_select_candidate(int nargs, { if (input_typeids[i] != UNKNOWNOID) { - if (IsBinaryCompatible(current_typeids[i], input_typeids[i])) + if (IsBinaryCoercible(input_typeids[i], current_typeids[i])) nmatch++; } } @@ -776,7 +774,7 @@ func_get_detail(List *funcname, Node *arg1 = lfirst(fargs); if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) || - IsBinaryCompatible(sourceType, targetType)) + IsBinaryCoercible(sourceType, targetType)) { /* Yup, it's a type coercion */ *funcid = InvalidOid; @@ -1120,8 +1118,7 @@ typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId) * actual arguments and argument types, do the necessary typecasting. */ static void -make_arguments(ParseState *pstate, - int nargs, +make_arguments(int nargs, List *fargs, Oid *input_typeids, Oid *function_typeids) @@ -1136,11 +1133,11 @@ make_arguments(ParseState *pstate, /* types don't match? then force coercion using a function call... */ if (input_typeids[i] != function_typeids[i]) { - lfirst(current_fargs) = coerce_type(pstate, - lfirst(current_fargs), + lfirst(current_fargs) = coerce_type(lfirst(current_fargs), input_typeids[i], - function_typeids[i], -1, - false); + function_typeids[i], + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST); } } } @@ -1179,9 +1176,7 @@ setup_field_select(Node *input, char *attname, Oid relid) * NB: argument is expected to be transformed already, ie, not a RangeVar. */ static Node * -ParseComplexProjection(ParseState *pstate, - char *funcname, - Node *first_arg) +ParseComplexProjection(char *funcname, Node *first_arg) { Oid argtype = exprType(first_arg); Oid argrelid; diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 391694fa198523d8607684d6d67e2ecdb9fc4a4f..408fb4f11f6c1c310f35a8651db813590903b3f3 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -1,27 +1,22 @@ /*------------------------------------------------------------------------- * * parse_node.c - * various routines that make nodes for query plans + * various routines that make nodes for querytrees * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.68 2002/09/04 20:31:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.69 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include <ctype.h> -#include <errno.h> -#include <float.h> - #include "access/heapam.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" -#include "fmgr.h" #include "nodes/makefuncs.h" #include "parser/parsetree.h" #include "parser/parse_coerce.h" @@ -29,14 +24,11 @@ #include "parser/parse_node.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" -#include "parser/parse_target.h" -#include "parser/parse_type.h" #include "utils/builtins.h" -#include "utils/varbit.h" +#include "utils/int8.h" #include "utils/lsyscache.h" #include "utils/syscache.h" - -static bool fitsInFloat(Value *value); +#include "utils/varbit.h" /* make_parsestate() @@ -70,8 +62,8 @@ make_operand(Node *tree, Oid orig_typeId, Oid target_typeId) { /* must coerce? */ if (target_typeId != orig_typeId) - result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1, - false); + result = coerce_type(tree, orig_typeId, target_typeId, + COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else result = tree; } @@ -191,6 +183,7 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno) * arrayBase Already-transformed expression for the array as a whole * (may be NULL if we are handling an INSERT) * arrayType OID of array's datatype + * arrayTypMod typmod to be applied to array elements * indirection Untransformed list of subscripts (must not be NIL) * forceSlice If true, treat subscript as array slice in all cases * assignFrom NULL for array fetch, else transformed expression for source. @@ -199,6 +192,7 @@ ArrayRef * transformArraySubscripts(ParseState *pstate, Node *arrayBase, Oid arrayType, + int32 arrayTypMod, List *indirection, bool forceSlice, Node *assignFrom) @@ -286,8 +280,10 @@ transformArraySubscripts(ParseState *pstate, { subexpr = transformExpr(pstate, ai->lidx); /* If it's not int4 already, try to coerce */ - subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr), - INT4OID, -1, false); + subexpr = coerce_to_target_type(subexpr, exprType(subexpr), + INT4OID, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); if (subexpr == NULL) elog(ERROR, "array index expressions must be integers"); } @@ -306,8 +302,10 @@ transformArraySubscripts(ParseState *pstate, } subexpr = transformExpr(pstate, ai->uidx); /* If it's not int4 already, try to coerce */ - subexpr = CoerceTargetExpr(pstate, subexpr, exprType(subexpr), - INT4OID, -1, false); + subexpr = coerce_to_target_type(subexpr, exprType(subexpr), + INT4OID, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); if (subexpr == NULL) elog(ERROR, "array index expressions must be integers"); upperIndexpr = lappend(upperIndexpr, subexpr); @@ -323,19 +321,16 @@ transformArraySubscripts(ParseState *pstate, if (typesource != InvalidOid) { - if (typesource != typeneeded) - { - /* XXX fixme: need to get the array's atttypmod? */ - assignFrom = CoerceTargetExpr(pstate, assignFrom, - typesource, typeneeded, - -1, false); - if (assignFrom == NULL) - elog(ERROR, "Array assignment requires type '%s'" - " but expression is of type '%s'" - "\n\tYou will need to rewrite or cast the expression", - format_type_be(typeneeded), - format_type_be(typesource)); - } + assignFrom = coerce_to_target_type(assignFrom, typesource, + typeneeded, arrayTypMod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); + if (assignFrom == NULL) + elog(ERROR, "Array assignment requires type %s" + " but expression is of type %s" + "\n\tYou will need to rewrite or cast the expression", + format_type_be(typeneeded), + format_type_be(typesource)); } } @@ -344,7 +339,7 @@ transformArraySubscripts(ParseState *pstate, */ aref = makeNode(ArrayRef); aref->refrestype = resultType; /* XXX should save element type - * too */ + * OID too */ aref->refattrlength = type_struct_array->typlen; aref->refelemlength = type_struct_element->typlen; aref->refelembyval = type_struct_element->typbyval; @@ -373,21 +368,16 @@ transformArraySubscripts(ParseState *pstate, * resolution that we're not sure that it should be considered text. * Explicit "NULL" constants are also typed as UNKNOWN. * - * For integers and floats we produce int4, float8, or numeric depending - * on the value of the number. XXX In some cases it would be nice to take - * context into account when determining the type to convert to, but in - * other cases we can't delay the type choice. One possibility is to invent - * a dummy type "UNKNOWNNUMERIC" that's treated similarly to UNKNOWN; - * that would allow us to do the right thing in examples like a simple - * INSERT INTO table (numericcolumn) VALUES (1.234), since we wouldn't - * have to resolve the unknown type until we knew the destination column - * type. On the other hand UNKNOWN has considerable problems of its own. - * We would not like "SELECT 1.2 + 3.4" to claim it can't choose a type. + * For integers and floats we produce int4, int8, or numeric depending + * on the value of the number. XXX This should include int2 as well, + * but additional cleanup is needed before we can do that; else cases + * like "WHERE int4var = 42" will fail to be indexable. */ Const * make_const(Value *value) { Datum val; + int64 val64; Oid typeid; int typelen; bool typebyval; @@ -404,12 +394,13 @@ make_const(Value *value) break; case T_Float: - if (fitsInFloat(value)) + /* could be an oversize integer as well as a float ... */ + if (scanint8(strVal(value), true, &val64)) { - val = Float8GetDatum(floatVal(value)); + val = Int64GetDatum(val64); - typeid = FLOAT8OID; - typelen = sizeof(float8); + typeid = INT8OID; + typelen = sizeof(int64); typebyval = false; /* XXX might change someday */ } else @@ -470,46 +461,3 @@ make_const(Value *value) return con; } - -/* - * Decide whether a T_Float value fits in float8, or must be treated as - * type "numeric". We check the number of digits and check for overflow/ - * underflow. (With standard compilation options, Postgres' NUMERIC type - * can handle decimal exponents up to 1000, considerably more than most - * implementations of float8, so this is a sensible test.) - */ -static bool -fitsInFloat(Value *value) -{ - const char *ptr; - int ndigits; - char *endptr; - - /* - * Count digits, ignoring leading zeroes (but not trailing zeroes). - * DBL_DIG is the maximum safe number of digits for "double". - */ - ptr = strVal(value); - while (*ptr == '+' || *ptr == '-' || *ptr == '0' || *ptr == '.') - ptr++; - ndigits = 0; - for (; *ptr; ptr++) - { - if (isdigit((unsigned char) *ptr)) - ndigits++; - else if (*ptr == 'e' || *ptr == 'E') - break; /* don't count digits in exponent */ - } - if (ndigits > DBL_DIG) - return false; - - /* - * Use strtod() to check for overflow/underflow. - */ - errno = 0; - (void) strtod(strVal(value), &endptr); - if (*endptr != '\0' || errno != 0) - return false; - - return true; -} diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index ecf1a2abece4241a6fa36e4ce41d9c9f25635f39..776acc78bfae1729354e14cf15e8a75454f5f3b6 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.59 2002/09/04 20:31:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.60 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -273,7 +273,7 @@ oper_select_candidate(int nargs, current_candidate = current_candidate->next) { if (can_coerce_type(nargs, input_typeids, current_candidate->args, - false)) + COERCION_IMPLICIT)) { if (last_candidate == NULL) { @@ -362,7 +362,7 @@ oper_select_candidate(int nargs, { if (input_typeids[i] != UNKNOWNOID) { - if (IsBinaryCompatible(current_typeids[i], input_typeids[i])) + if (IsBinaryCoercible(input_typeids[i], current_typeids[i])) nmatch++; } } @@ -696,8 +696,8 @@ compatible_oper(List *op, Oid arg1, Oid arg2, bool noError) /* but is it good enough? */ opform = (Form_pg_operator) GETSTRUCT(optup); - if (IsBinaryCompatible(opform->oprleft, arg1) && - IsBinaryCompatible(opform->oprright, arg2)) + if (IsBinaryCoercible(arg1, opform->oprleft) && + IsBinaryCoercible(arg2, opform->oprright)) return optup; /* nope... */ diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index b9c5b6cb137da86a3b8875b63a83162711718c67..18d11cc7f5ac55c5f0ba87eab9a63f66389b66ce 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.89 2002/09/04 20:31:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.90 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -274,6 +274,7 @@ updateTargetListEntry(ParseState *pstate, aref = transformArraySubscripts(pstate, arrayBase, attrtype, + attrtypmod, indirection, pstate->p_is_insert, tle->expr); @@ -284,30 +285,21 @@ updateTargetListEntry(ParseState *pstate, /* * For normal non-subscripted target column, do type checking and * coercion. But accept InvalidOid, which indicates the source is - * a NULL constant. + * a NULL constant. (XXX is that still true?) */ if (type_id != InvalidOid) { - if (type_id != attrtype) - { - tle->expr = CoerceTargetExpr(pstate, tle->expr, type_id, - attrtype, attrtypmod, - false); - if (tle->expr == NULL) - elog(ERROR, "column \"%s\" is of type '%s'" - " but expression is of type '%s'" - "\n\tYou will need to rewrite or cast the expression", - colname, - format_type_be(attrtype), - format_type_be(type_id)); - } - - /* - * If the target is a fixed-length type, it may need a length - * coercion as well as a type coercion. - */ - tle->expr = coerce_type_typmod(pstate, tle->expr, - attrtype, attrtypmod); + tle->expr = coerce_to_target_type(tle->expr, type_id, + attrtype, attrtypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); + if (tle->expr == NULL) + elog(ERROR, "column \"%s\" is of type %s" + " but expression is of type %s" + "\n\tYou will need to rewrite or cast the expression", + colname, + format_type_be(attrtype), + format_type_be(type_id)); } } @@ -324,46 +316,6 @@ updateTargetListEntry(ParseState *pstate, } -Node * -CoerceTargetExpr(ParseState *pstate, - Node *expr, - Oid type_id, - Oid attrtype, - int32 attrtypmod, - bool isExplicit) -{ - if (can_coerce_type(1, &type_id, &attrtype, isExplicit)) - expr = coerce_type(pstate, expr, type_id, attrtype, attrtypmod, - isExplicit); - -#ifndef DISABLE_STRING_HACKS - - /* - * string hacks to get transparent conversions w/o explicit - * conversions - */ - else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID)) - { - Oid text_id = TEXTOID; - - if (type_id == TEXTOID) - { - } - else if (can_coerce_type(1, &type_id, &text_id, isExplicit)) - expr = coerce_type(pstate, expr, type_id, text_id, attrtypmod, - isExplicit); - else - expr = NULL; - } -#endif - - else - expr = NULL; - - return expr; -} - - /* * checkInsertTargets - * generate a list of INSERT column targets if not supplied, or diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 95fc726227f802bb7619bf81e11465b6c0f2f0a8..0b2d839eb3f9570a21c7187f659ff1c1b896f228 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 - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.109 2002/09/11 14:48:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.110 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,6 @@ #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_oper.h" -#include "parser/parse_target.h" #include "parser/parse_type.h" #include "parser/parsetree.h" #include "rewrite/rewriteHandler.h" @@ -474,29 +473,21 @@ build_column_default(Relation rel, int attrno) */ exprtype = exprType(expr); - if (exprtype != atttype) - { - expr = CoerceTargetExpr(NULL, expr, exprtype, - atttype, atttypmod, false); - - /* - * This really shouldn't fail; should have checked the default's - * type when it was created ... - */ - if (expr == NULL) - elog(ERROR, "Column \"%s\" is of type %s" - " but default expression is of type %s" - "\n\tYou will need to rewrite or cast the expression", - NameStr(att_tup->attname), - format_type_be(atttype), - format_type_be(exprtype)); - } - + expr = coerce_to_target_type(expr, exprtype, + atttype, atttypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST); /* - * If the column is a fixed-length type, it may need a length coercion - * as well as a type coercion. + * This really shouldn't fail; should have checked the default's + * type when it was created ... */ - expr = coerce_type_typmod(NULL, expr, atttype, atttypmod); + if (expr == NULL) + elog(ERROR, "Column \"%s\" is of type %s" + " but default expression is of type %s" + "\n\tYou will need to rewrite or cast the expression", + NameStr(att_tup->attname), + format_type_be(atttype), + format_type_be(exprtype)); return expr; } diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 5d53eca999fdea5f0fc2cc7599cbd0f90896110f..f864338897037c3a63e6bcebb4cf496b5ac79da8 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.80 2002/09/04 20:31:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.81 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "access/tupmacs.h" #include "catalog/catalog.h" #include "catalog/pg_type.h" +#include "parser/parse_coerce.h" #include "utils/array.h" #include "utils/memutils.h" #include "utils/syscache.h" @@ -755,6 +756,72 @@ array_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(retval); } +/*------------------------------------------------------------------------- + * 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; + FmgrInfo *element_finfo; + 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. + */ + if (fmgr_info->fn_extra == NULL) + { + Oid funcId; + int nargs; + + fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt, + sizeof(FmgrInfo)); + element_finfo = (FmgrInfo *) fmgr_info->fn_extra; + + funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v), &nargs); + + if (OidIsValid(funcId)) + fmgr_info_cxt(funcId, element_finfo, fmgr_info->fn_mcxt); + else + element_finfo->fn_oid = InvalidOid; + } + else + element_finfo = (FmgrInfo *) fmgr_info->fn_extra; + + /* + * 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 (element_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 = element_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" diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 267ad821037d10b89cc26c46b4f767edce8d7a34..8a346cd8b838be5520de718356ed2d6dbf59b6e6 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/int8.c,v 1.41 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int8.c,v 1.42 2002/09/18 21:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,14 +48,16 @@ * Formatting and conversion routines. *---------------------------------------------------------*/ -/* int8in() +/* + * scanint8 --- try to parse a string into an int8. + * + * If errorOK is false, elog a useful error message if the string is bad. + * If errorOK is true, just return "false" for bad input. */ -Datum -int8in(PG_FUNCTION_ARGS) +bool +scanint8(const char *str, bool errorOK, int64 *result) { - char *str = PG_GETARG_CSTRING(0); - int64 result; - char *ptr = str; + const char *ptr = str; int64 tmp = 0; int sign = 1; @@ -63,8 +65,11 @@ int8in(PG_FUNCTION_ARGS) * Do our own scan, rather than relying on sscanf which might be * broken for long long. */ - while (*ptr && isspace((unsigned char) *ptr)) /* skip leading spaces */ + + /* skip leading spaces */ + while (*ptr && isspace((unsigned char) *ptr)) ptr++; + /* handle sign */ if (*ptr == '-') { @@ -79,28 +84,61 @@ int8in(PG_FUNCTION_ARGS) #ifndef INT64_IS_BUSTED if (strcmp(ptr, "9223372036854775808") == 0) { - result = -INT64CONST(0x7fffffffffffffff) - 1; - PG_RETURN_INT64(result); + *result = -INT64CONST(0x7fffffffffffffff) - 1; + return true; } #endif } else if (*ptr == '+') ptr++; - if (!isdigit((unsigned char) *ptr)) /* require at least one digit */ - elog(ERROR, "Bad int8 external representation \"%s\"", str); - while (*ptr && isdigit((unsigned char) *ptr)) /* process digits */ + + /* require at least one digit */ + if (!isdigit((unsigned char) *ptr)) + { + if (errorOK) + return false; + else + elog(ERROR, "Bad int8 external representation \"%s\"", str); + } + + /* process digits */ + while (*ptr && isdigit((unsigned char) *ptr)) { int64 newtmp = tmp * 10 + (*ptr++ - '0'); if ((newtmp / 10) != tmp) /* overflow? */ - elog(ERROR, "int8 value out of range: \"%s\"", str); + { + if (errorOK) + return false; + else + elog(ERROR, "int8 value out of range: \"%s\"", str); + } tmp = newtmp; } - if (*ptr) /* trailing junk? */ - elog(ERROR, "Bad int8 external representation \"%s\"", str); - result = (sign < 0) ? -tmp : tmp; + /* trailing junk? */ + if (*ptr) + { + if (errorOK) + return false; + else + elog(ERROR, "Bad int8 external representation \"%s\"", str); + } + *result = (sign < 0) ? -tmp : tmp; + + return true; +} + +/* int8in() + */ +Datum +int8in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + int64 result; + + (void) scanint8(str, false, &result); PG_RETURN_INT64(result); } @@ -747,7 +785,7 @@ i8tod(PG_FUNCTION_ARGS) } /* dtoi8() - * Convert double float to 8-byte integer. + * Convert float8 to 8-byte integer. */ Datum dtoi8(PG_FUNCTION_ARGS) @@ -771,8 +809,66 @@ dtoi8(PG_FUNCTION_ARGS) PG_RETURN_INT64(result); } -/* text_int8() +Datum +i8tof(PG_FUNCTION_ARGS) +{ + int64 val = PG_GETARG_INT64(0); + float4 result; + + result = val; + + PG_RETURN_FLOAT4(result); +} + +/* ftoi8() + * Convert float4 to 8-byte integer. */ +Datum +ftoi8(PG_FUNCTION_ARGS) +{ + float4 val = PG_GETARG_FLOAT4(0); + int64 result; + float8 dval; + + /* Round val to nearest integer (but it's still in float form) */ + dval = rint(val); + + /* + * Does it fit in an int64? Avoid assuming that we have handy + * constants defined for the range boundaries, instead test for + * overflow by reverse-conversion. + */ + result = (int64) dval; + + if ((float8) result != dval) + elog(ERROR, "Floating point conversion to int8 is out of range"); + + PG_RETURN_INT64(result); +} + +Datum +i8tooid(PG_FUNCTION_ARGS) +{ + int64 val = PG_GETARG_INT64(0); + Oid result; + + result = (Oid) val; + + /* Test for overflow by reverse-conversion. */ + if ((int64) result != val) + elog(ERROR, "int8 conversion to OID is out of range"); + + PG_RETURN_OID(result); +} + +Datum +oidtoi8(PG_FUNCTION_ARGS) +{ + Oid val = PG_GETARG_OID(0); + + PG_RETURN_INT64((int64) val); +} + Datum text_int8(PG_FUNCTION_ARGS) { @@ -793,9 +889,6 @@ text_int8(PG_FUNCTION_ARGS) return result; } - -/* int8_text() - */ Datum int8_text(PG_FUNCTION_ARGS) { diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 228c43c6c4644f1f31fa3c9e7ab83cff29fadd0c..4ea0fec1c1a13e71ae41bfbffc06caffe2b765d3 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -5,7 +5,7 @@ * * 1998 Jan Wieck * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.53 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.54 2002/09/18 21:35:22 tgl Exp $ * * ---------- */ @@ -1709,6 +1709,50 @@ numeric_float4(PG_FUNCTION_ARGS) } +Datum +text_numeric(PG_FUNCTION_ARGS) +{ + text *str = PG_GETARG_TEXT_P(0); + int len; + char *s; + Datum result; + + len = (VARSIZE(str) - VARHDRSZ); + s = palloc(len + 1); + memcpy(s, VARDATA(str), len); + *(s + len) = '\0'; + + result = DirectFunctionCall3(numeric_in, CStringGetDatum(s), + ObjectIdGetDatum(0), Int32GetDatum(-1)); + + pfree(s); + + return result; +} + +Datum +numeric_text(PG_FUNCTION_ARGS) +{ + /* val is numeric, but easier to leave it as Datum */ + Datum val = PG_GETARG_DATUM(0); + char *s; + int len; + text *result; + + s = DatumGetCString(DirectFunctionCall1(numeric_out, val)); + len = strlen(s); + + result = (text *) palloc(VARHDRSZ + len); + + VARATT_SIZEP(result) = len + VARHDRSZ; + memcpy(VARDATA(result), s, len); + + pfree(s); + + PG_RETURN_TEXT_P(result); +} + + /* ---------------------------------------------------------------------- * * Aggregate functions diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 22c93c431a068e37decdd61b8d25020de9d173d3..638f5293ed2d7a3f2f2b1e8c298dae8f3193d7e6 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.75 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.76 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,7 +49,7 @@ static void parseNameAndArgTypes(const char *string, const char *caller, /* * regprocin - converts "proname" to proc OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_proc entry. @@ -71,15 +71,8 @@ regprocin(PG_FUNCTION_ARGS) pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); - result = (RegProcedure) GetSysCacheOid(PROCOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!RegProcedureIsValid(result)) - elog(ERROR, "No procedure with oid %s", pro_name_or_oid); PG_RETURN_OID(result); } @@ -211,7 +204,7 @@ regprocout(PG_FUNCTION_ARGS) /* * regprocedurein - converts "proname(args)" to proc OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_proc entry. @@ -235,15 +228,8 @@ regprocedurein(PG_FUNCTION_ARGS) pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); - result = (RegProcedure) GetSysCacheOid(PROCOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!RegProcedureIsValid(result)) - elog(ERROR, "No procedure with oid %s", pro_name_or_oid); PG_RETURN_OID(result); } @@ -361,7 +347,7 @@ regprocedureout(PG_FUNCTION_ARGS) /* * regoperin - converts "oprname" to operator OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '0' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_operator entry. @@ -383,15 +369,8 @@ regoperin(PG_FUNCTION_ARGS) opr_name_or_oid[0] <= '9' && strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(opr_name_or_oid))); - result = GetSysCacheOid(OPEROID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No operator with oid %s", opr_name_or_oid); PG_RETURN_OID(result); } @@ -531,7 +510,7 @@ regoperout(PG_FUNCTION_ARGS) /* * regoperatorin - converts "oprname(args)" to operator OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '0' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_operator entry. @@ -556,15 +535,8 @@ regoperatorin(PG_FUNCTION_ARGS) opr_name_or_oid[0] <= '9' && strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(opr_name_or_oid))); - result = GetSysCacheOid(OPEROID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No operator with oid %s", opr_name_or_oid); PG_RETURN_OID(result); } @@ -698,7 +670,7 @@ regoperatorout(PG_FUNCTION_ARGS) /* * regclassin - converts "classname" to class OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_class entry. @@ -719,15 +691,8 @@ regclassin(PG_FUNCTION_ARGS) class_name_or_oid[0] <= '9' && strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(class_name_or_oid))); - result = GetSysCacheOid(RELOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No class with oid %s", class_name_or_oid); PG_RETURN_OID(result); } @@ -843,7 +808,7 @@ regclassout(PG_FUNCTION_ARGS) /* * regtypein - converts "typename" to type OID * - * We also accept a numeric OID, mostly for historical reasons. + * We also accept a numeric OID, for symmetry with the output routine. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. @@ -870,15 +835,8 @@ regtypein(PG_FUNCTION_ARGS) typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { - Oid searchOid; - - searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, + result = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); - result = GetSysCacheOid(TYPEOID, - ObjectIdGetDatum(searchOid), - 0, 0, 0); - if (!OidIsValid(result)) - elog(ERROR, "No type with oid %s", typ_name_or_oid); PG_RETURN_OID(result); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 740dde36dd49ac7670f2299e7da9dafd0c38dedf..9f21a609f3dfc179d87e9da46ce9e127690b99f7 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.121 2002/09/04 20:31:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.122 2002/09/18 21:35:23 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -152,7 +152,6 @@ static void get_oper_expr(Expr *expr, deparse_context *context); static void get_func_expr(Expr *expr, deparse_context *context); static void get_agg_expr(Aggref *aggref, deparse_context *context); static Node *strip_type_coercion(Node *expr, Oid resultType); -static void get_tle_expr(TargetEntry *tle, deparse_context *context); static void get_const_expr(Const *constval, deparse_context *context); static void get_sublink_expr(Node *node, deparse_context *context); static void get_from_clause(Query *query, deparse_context *context); @@ -1430,7 +1429,6 @@ get_basic_select_query(Query *query, deparse_context *context, sep = ", "; colno++; - /* Do NOT use get_tle_expr here; see its comments! */ get_rule_expr(tle->expr, context); /* @@ -1644,7 +1642,7 @@ get_insert_query_def(Query *query, deparse_context *context) appendStringInfo(buf, sep); sep = ", "; - get_tle_expr(tle, context); + get_rule_expr(tle->expr, context); } appendStringInfoChar(buf, ')'); } @@ -1694,7 +1692,7 @@ get_update_query_def(Query *query, deparse_context *context) if (!tleIsArrayAssign(tle)) appendStringInfo(buf, "%s = ", quote_identifier(tle->resdom->resname)); - get_tle_expr(tle, context); + get_rule_expr(tle->expr, context); } /* Add the FROM clause if needed */ @@ -2106,12 +2104,29 @@ get_rule_expr(Node *node, deparse_context *context) case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; + Node *arg = relabel->arg; - appendStringInfoChar(buf, '('); - get_rule_expr(relabel->arg, context); - appendStringInfo(buf, ")::%s", + if (relabel->relabelformat == COERCE_IMPLICIT_CAST) + { + /* don't show an implicit cast */ + get_rule_expr(arg, context); + } + 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); + + appendStringInfoChar(buf, '('); + get_rule_expr(arg, context); + appendStringInfo(buf, ")::%s", format_type_with_typemod(relabel->resulttype, - relabel->resulttypmod)); + relabel->resulttypmod)); + } } break; @@ -2305,21 +2320,33 @@ get_func_expr(Expr *expr, deparse_context *context) StringInfo buf = context->buf; Func *func = (Func *) (expr->oper); Oid funcoid = func->funcid; - int32 coercedTypmod; Oid argtypes[FUNC_MAX_ARGS]; int nargs; List *l; char *sep; /* - * Check to see if function is a length-coercion function for some - * datatype. If so, display the operation as a type cast. + * If the function call came from an implicit coercion, then just show + * the first argument. + */ + if (func->funcformat == COERCE_IMPLICIT_CAST) + { + get_rule_expr((Node *) lfirst(expr->args), context); + return; + } + + /* + * If the function call came from an explicit cast, then show + * the first argument plus an explicit cast operation. */ - if (exprIsLengthCoercion((Node *) expr, &coercedTypmod)) + if (func->funcformat == COERCE_EXPLICIT_CAST) { Node *arg = lfirst(expr->args); - Oid rettype = get_func_rettype(funcoid); - char *typdesc; + Oid rettype = expr->typeOid; + int32 coercedTypmod; + + /* 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 @@ -2331,17 +2358,8 @@ get_func_expr(Expr *expr, deparse_context *context) appendStringInfoChar(buf, '('); get_rule_expr(arg, context); - - /* - * Show typename with appropriate length decoration. Note that - * since exprIsLengthCoercion succeeded, the function's output - * type is the right thing to report. Also note we don't need to - * quote the result of format_type_with_typemod: it takes care of - * double-quoting any identifier that needs it. - */ - typdesc = format_type_with_typemod(rettype, coercedTypmod); - appendStringInfo(buf, ")::%s", typdesc); - pfree(typdesc); + appendStringInfo(buf, ")::%s", + format_type_with_typemod(rettype, coercedTypmod)); return; } @@ -2393,15 +2411,14 @@ get_agg_expr(Aggref *aggref, deparse_context *context) /* * strip_type_coercion - * Strip any type coercions at the top of the given expression tree, - * as long as they are coercions to the given datatype. + * Strip any type coercion at the top of the given expression tree, + * if it is a coercion to the given datatype. * - * A RelabelType node is always a type coercion. A function call is - * also considered a type coercion if it has one argument and there is - * a cast declared that uses it. + * 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. * - * XXX It'd be better if the parsetree retained some explicit indication - * of the coercion, so we didn't need these heuristics. + * Note: avoid stripping a length-coercion node, since two successive + * coercions to different lengths aren't a no-op. */ static Node * strip_type_coercion(Node *expr, Oid resultType) @@ -2409,101 +2426,30 @@ strip_type_coercion(Node *expr, Oid resultType) if (expr == NULL || exprType(expr) != resultType) return expr; - if (IsA(expr, RelabelType)) - return strip_type_coercion(((RelabelType *) expr)->arg, resultType); + if (IsA(expr, RelabelType) && + ((RelabelType *) expr)->resulttypmod == -1) + return ((RelabelType *) expr)->arg; if (IsA(expr, Expr) && ((Expr *) expr)->opType == FUNC_EXPR) { - Func *func; - HeapTuple procTuple; - HeapTuple castTuple; - Form_pg_proc procStruct; - Form_pg_cast castStruct; + Func *func = (Func *) (((Expr *) expr)->oper); - func = (Func *) (((Expr *) expr)->oper); Assert(IsA(func, Func)); - if (length(((Expr *) expr)->args) != 1) - return expr; - /* Lookup the function in pg_proc */ - procTuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(func->funcid), - 0, 0, 0); - if (!HeapTupleIsValid(procTuple)) - elog(ERROR, "cache lookup for proc %u failed", func->funcid); - procStruct = (Form_pg_proc) GETSTRUCT(procTuple); - /* Double-check func has one arg and correct result type */ - if (procStruct->pronargs != 1 || - procStruct->prorettype != resultType) - { - ReleaseSysCache(procTuple); - return expr; - } - /* See if function has is actually declared as a cast */ - castTuple = SearchSysCache(CASTSOURCETARGET, - ObjectIdGetDatum(procStruct->proargtypes[0]), - ObjectIdGetDatum(procStruct->prorettype), - 0, 0); - if (!HeapTupleIsValid(castTuple)) - { - ReleaseSysCache(procTuple); - return expr; - } - /* It must also be an implicit cast. */ - castStruct = (Form_pg_cast) GETSTRUCT(castTuple); - if (!castStruct->castimplicit) - { - ReleaseSysCache(procTuple); - ReleaseSysCache(castTuple); + 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; - } - /* Okay, it is indeed a type-coercion function */ - ReleaseSysCache(procTuple); - ReleaseSysCache(castTuple); - return strip_type_coercion(lfirst(((Expr *) expr)->args), resultType); + + return (Node *) lfirst(((Expr *) expr)->args); } return expr; } -/* ---------- - * get_tle_expr - * - * In an INSERT or UPDATE targetlist item, the parser may have inserted - * a length-coercion function call to coerce the value to the right - * length for the target column. We want to suppress the output of - * that function call, otherwise dump/reload/dump... would blow up the - * expression by adding more and more layers of length-coercion calls. - * - * As of 7.0, this hack is no longer absolutely essential, because the parser - * is now smart enough not to add a redundant length coercion function call. - * But we still suppress the function call just for neatness of displayed - * rules. - * - * Note that this hack must NOT be applied to SELECT targetlist items; - * any length coercion appearing there is something the user actually wrote. - * ---------- - */ -static void -get_tle_expr(TargetEntry *tle, deparse_context *context) -{ - Expr *expr = (Expr *) (tle->expr); - int32 coercedTypmod; - - /* - * If top level is a length coercion to the correct length, suppress - * it; else dump the expression normally. - */ - if (tle->resdom->restypmod >= 0 && - exprIsLengthCoercion((Node *) expr, &coercedTypmod) && - coercedTypmod == tle->resdom->restypmod) - get_rule_expr((Node *) lfirst(expr->args), context); - else - get_rule_expr(tle->expr, context); -} - - /* ---------- * get_const_expr * @@ -2518,6 +2464,8 @@ get_const_expr(Const *constval, deparse_context *context) Form_pg_type typeStruct; char *extval; char *valptr; + bool isfloat = false; + bool needlabel; if (constval->constisnull) { @@ -2563,8 +2511,12 @@ get_const_expr(Const *constval, deparse_context *context) * NaN, so we need not get too crazy about pattern * matching here. */ - if (strspn(extval, "0123456789 +-eE.") == strlen(extval)) + if (strspn(extval, "0123456789+-eE.") == strlen(extval)) + { appendStringInfo(buf, extval); + if (strcspn(extval, "eE.") != strlen(extval)) + isfloat = true; /* it looks like a float */ + } else appendStringInfo(buf, "'%s'", extval); } @@ -2609,20 +2561,30 @@ get_const_expr(Const *constval, deparse_context *context) pfree(extval); + /* + * Append ::typename unless the constant will be implicitly typed as + * the right type when it is read in. XXX this code has to be kept + * in sync with the behavior of the parser, especially make_const. + */ switch (constval->consttype) { case BOOLOID: case INT4OID: - case FLOAT8OID: case UNKNOWNOID: /* These types can be left unlabeled */ + needlabel = false; + break; + case NUMERICOID: + /* Float-looking constants will be typed as numeric */ + needlabel = !isfloat; break; default: - appendStringInfo(buf, "::%s", - format_type_with_typemod(constval->consttype, - -1)); + needlabel = true; break; } + if (needlabel) + appendStringInfo(buf, "::%s", + format_type_with_typemod(constval->consttype, -1)); ReleaseSysCache(typetup); } diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index f0c31a3961b4a45f529bb36befd941508a0c62ab..715a99863fda2a1deb3a7aa4f97c69c3940ce48c 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 - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varbit.c,v 1.25 2002/09/04 20:31:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varbit.c,v 1.26 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,6 +35,11 @@ * data section -- private data section for the bits data structures * bitlength -- length of the bit string in bits * bitdata -- bit string, most significant byte first + * + * The length of the bitdata vector should always be exactly as many + * bytes as are needed for the given bitlength. If the bitlength is + * not a multiple of 8, the extra low-order padding bits of the last + * byte must be zeroes. *---------- */ @@ -104,7 +109,7 @@ bit_in(PG_FUNCTION_ARGS) len = VARBITTOTALLEN(atttypmod); result = (VarBit *) palloc(len); /* set to 0 so that *r is always initialised and string is zero-padded */ - memset(result, 0, len); + MemSet(result, 0, len); VARATT_SIZEP(result) = len; VARBITLEN(result) = atttypmod; @@ -203,50 +208,52 @@ bit_out(PG_FUNCTION_ARGS) /* bit() * Converts a bit() type to a specific internal length. * len is the bitlength specified in the column definition. + * + * If doing implicit cast, raise error when source data is wrong length. + * If doing explicit cast, silently truncate or zero-pad to specified length. */ Datum bit(PG_FUNCTION_ARGS) { VarBit *arg = PG_GETARG_VARBIT_P(0); int32 len = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + VarBit *result; + int rlen; + int ipad; + bits8 mask; /* No work if typmod is invalid or supplied data matches it already */ if (len <= 0 || len == VARBITLEN(arg)) PG_RETURN_VARBIT_P(arg); - else + + if (!isExplicit) elog(ERROR, "Bit string length %d does not match type BIT(%d)", VARBITLEN(arg), len); - return 0; /* quiet compiler */ -} -/* _bit() - * Converts an array of bit() elements to a specific internal length. - * len is the bitlength specified in the column definition. - */ -Datum -_bit(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; + rlen = VARBITTOTALLEN(len); + result = (VarBit *) palloc(rlen); + /* set to 0 so that string is zero-padded */ + MemSet(result, 0, rlen); + VARATT_SIZEP(result) = rlen; + VARBITLEN(result) = len; + + memcpy(VARBITS(result), VARBITS(arg), + Min(VARBITBYTES(result), VARBITBYTES(arg))); /* - * Since bit() is a built-in function, we should only need to look it - * up once per run. + * Make sure last byte is zero-padded if needed. This is useless but + * safe if source data was shorter than target length (we assume the + * last byte of the source data was itself correctly zero-padded). */ - static FmgrInfo bit_finfo; - - if (bit_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_BIT, &bit_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &bit_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); + ipad = VARBITPAD(result); + if (ipad > 0) + { + mask = BITMASK << ipad; + *(VARBITS(result) + VARBITBYTES(result) - 1) &= mask; + } - return array_map(&locfcinfo, BITOID, BITOID); + PG_RETURN_VARBIT_P(result); } /* @@ -311,7 +318,7 @@ varbit_in(PG_FUNCTION_ARGS) len = VARBITTOTALLEN(bitlen); result = (VarBit *) palloc(len); /* set to 0 so that *r is always initialised and string is zero-padded */ - memset(result, 0, len); + MemSet(result, 0, len); VARATT_SIZEP(result) = len; VARBITLEN(result) = Min(bitlen, atttypmod); @@ -406,20 +413,26 @@ varbit_out(PG_FUNCTION_ARGS) /* varbit() * Converts a varbit() type to a specific internal length. * len is the maximum bitlength specified in the column definition. + * + * If doing implicit cast, raise error when source data is too long. + * If doing explicit cast, silently truncate to max length. */ Datum varbit(PG_FUNCTION_ARGS) { VarBit *arg = PG_GETARG_VARBIT_P(0); int32 len = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); VarBit *result; int rlen; + int ipad; + bits8 mask; /* No work if typmod is invalid or supplied data matches it already */ if (len <= 0 || len >= VARBITLEN(arg)) PG_RETURN_VARBIT_P(arg); - if (len < VARBITLEN(arg)) + if (!isExplicit) elog(ERROR, "Bit string too long for type BIT VARYING(%d)", len); rlen = VARBITTOTALLEN(len); @@ -429,37 +442,15 @@ varbit(PG_FUNCTION_ARGS) memcpy(VARBITS(result), VARBITS(arg), VARBITBYTES(result)); - PG_RETURN_VARBIT_P(result); -} - -/* _varbit() - * Converts an array of bit() elements to a specific internal length. - * len is the maximum bitlength specified in the column definition. - */ -Datum -_varbit(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; - - /* - * Since varbit() is a built-in function, we should only need to look - * it up once per run. - */ - static FmgrInfo varbit_finfo; - - if (varbit_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_VARBIT, &varbit_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &varbit_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); + /* Make sure last byte is zero-padded if needed */ + ipad = VARBITPAD(result); + if (ipad > 0) + { + mask = BITMASK << ipad; + *(VARBITS(result) + VARBITBYTES(result) - 1) &= mask; + } - return array_map(&locfcinfo, VARBITOID, VARBITOID); + PG_RETURN_VARBIT_P(result); } @@ -978,7 +969,7 @@ bitshiftleft(PG_FUNCTION_ARGS) /* If we shifted all the bits out, return an all-zero string */ if (shft >= VARBITLEN(arg)) { - memset(r, 0, VARBITBYTES(arg)); + MemSet(r, 0, VARBITBYTES(arg)); PG_RETURN_VARBIT_P(result); } @@ -991,7 +982,7 @@ bitshiftleft(PG_FUNCTION_ARGS) /* Special case: we can do a memcpy */ len = VARBITBYTES(arg) - byte_shift; memcpy(r, p, len); - memset(r + len, 0, byte_shift); + MemSet(r + len, 0, byte_shift); } else { @@ -1037,7 +1028,7 @@ bitshiftright(PG_FUNCTION_ARGS) /* If we shifted all the bits out, return an all-zero string */ if (shft >= VARBITLEN(arg)) { - memset(r, 0, VARBITBYTES(arg)); + MemSet(r, 0, VARBITBYTES(arg)); PG_RETURN_VARBIT_P(result); } @@ -1046,7 +1037,7 @@ bitshiftright(PG_FUNCTION_ARGS) p = VARBITS(arg); /* Set the first part of the result to 0 */ - memset(r, 0, byte_shift); + MemSet(r, 0, byte_shift); r += byte_shift; if (ishift == 0) diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 780daf756555dcb3b2271f228265ce8c5c7a8c67..03579f437b62b15ba79ad1382d3efdc934d2df90 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.94 2002/09/04 20:31:29 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.95 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -165,21 +165,28 @@ bpcharout(PG_FUNCTION_ARGS) /* - * Converts a CHARACTER type to the specified size. maxlen is the new - * declared length plus VARHDRSZ bytes. Truncation - * rules see bpcharin() above. + * Converts a CHARACTER type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to char(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error unless extra characters are + * all spaces. (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) */ Datum bpchar(PG_FUNCTION_ARGS) { BpChar *source = PG_GETARG_BPCHAR_P(0); int32 maxlen = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); BpChar *result; int32 len; char *r; char *s; int i; - int charlen; /* number of charcters in the input string * + VARHDRSZ */ @@ -188,7 +195,7 @@ bpchar(PG_FUNCTION_ARGS) charlen = pg_mbstrlen_with_len(VARDATA(source), len - VARHDRSZ) + VARHDRSZ; /* No work if typmod is invalid or supplied data matches it already */ - if (maxlen < (int32) VARHDRSZ || len == maxlen) + if (maxlen < (int32) VARHDRSZ || charlen == maxlen) PG_RETURN_BPCHAR_P(source); if (charlen > maxlen) @@ -199,10 +206,13 @@ bpchar(PG_FUNCTION_ARGS) maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ, maxlen - VARHDRSZ) + VARHDRSZ; - for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++) - if (*(VARDATA(source) + i) != ' ') - elog(ERROR, "value too long for type character(%d)", - maxlen - VARHDRSZ); + if (!isExplicit) + { + for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++) + if (*(VARDATA(source) + i) != ' ') + elog(ERROR, "value too long for type character(%d)", + maxlen - VARHDRSZ); + } len = maxmblen; @@ -238,37 +248,6 @@ bpchar(PG_FUNCTION_ARGS) } -/* _bpchar() - * Converts an array of char() elements to a specific internal length. - * len is the length specified in () plus VARHDRSZ bytes. - */ -Datum -_bpchar(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; - - /* - * Since bpchar() is a built-in function, we should only need to look - * it up once per run. - */ - static FmgrInfo bpchar_finfo; - - if (bpchar_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_BPCHAR, &bpchar_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &bpchar_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); - - return array_map(&locfcinfo, BPCHAROID, BPCHAROID); -} - - /* char_bpchar() * Convert char to bpchar(1). */ @@ -354,9 +333,9 @@ name_bpchar(PG_FUNCTION_ARGS) * Note that atttypmod is regarded as the number of characters, which * is not necessarily the same as the number of bytes. * - * If the C string is too long, - * raise an error, unless the extra characters are spaces, in which - * case they're truncated. (per SQL) */ + * If the C string is too long, raise an error, unless the extra characters + * are spaces, in which case they're truncated. (per SQL) + */ Datum varcharin(PG_FUNCTION_ARGS) { @@ -428,17 +407,26 @@ varcharout(PG_FUNCTION_ARGS) /* - * Converts a VARCHAR type to the specified size. maxlen is the new - * declared length plus VARHDRSZ bytes. Truncation - * rules see varcharin() above. + * Converts a VARCHAR type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to varchar(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error unless extra characters are + * all spaces. (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) */ Datum varchar(PG_FUNCTION_ARGS) { VarChar *source = PG_GETARG_VARCHAR_P(0); int32 maxlen = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); VarChar *result; int32 len; + size_t maxmblen; int i; len = VARSIZE(source); @@ -448,21 +436,19 @@ varchar(PG_FUNCTION_ARGS) /* only reach here if string is too long... */ - { - size_t maxmblen; - - /* truncate multibyte string preserving multibyte boundary */ - maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ, - maxlen - VARHDRSZ) + VARHDRSZ; + /* truncate multibyte string preserving multibyte boundary */ + maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ, + maxlen - VARHDRSZ); - for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++) + if (!isExplicit) + { + for (i = maxmblen; i < len - VARHDRSZ; i++) if (*(VARDATA(source) + i) != ' ') elog(ERROR, "value too long for type character varying(%d)", maxlen - VARHDRSZ); - - len = maxmblen; } + len = maxmblen + VARHDRSZ; result = palloc(len); VARATT_SIZEP(result) = len; memcpy(VARDATA(result), VARDATA(source), len - VARHDRSZ); @@ -471,38 +457,6 @@ varchar(PG_FUNCTION_ARGS) } -/* _varchar() - * Converts an array of varchar() elements to the specified size. - * len is the length specified in () plus VARHDRSZ bytes. - */ -Datum -_varchar(PG_FUNCTION_ARGS) -{ - ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); - int32 len = PG_GETARG_INT32(1); - FunctionCallInfoData locfcinfo; - - /* - * Since varchar() is a built-in function, we should only need to look - * it up once per run. - */ - static FmgrInfo varchar_finfo; - - if (varchar_finfo.fn_oid == InvalidOid) - fmgr_info_cxt(F_VARCHAR, &varchar_finfo, TopMemoryContext); - - MemSet(&locfcinfo, 0, sizeof(locfcinfo)); - locfcinfo.flinfo = &varchar_finfo; - locfcinfo.nargs = 2; - /* We assume we are "strict" and need not worry about null inputs */ - locfcinfo.arg[0] = PointerGetDatum(v); - locfcinfo.arg[1] = Int32GetDatum(len); - - return array_map(&locfcinfo, VARCHAROID, VARCHAROID); -} - - - /***************************************************************************** * Exported functions *****************************************************************************/ diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 2672ed3aada445c14854bc01796a21e006933481..c8a038d8a7d752fd63bbf9df5c049e17d20d2fd5 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.83 2002/09/04 20:31:30 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.84 2002/09/18 21:35:23 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -1073,51 +1073,6 @@ getBaseType(Oid typid) return typid; } -/* - * getBaseTypeMod - * If the given type is a domain, return the typmod it applies to - * its base type; otherwise return the specified original typmod. - */ -int32 -getBaseTypeMod(Oid typid, int32 typmod) -{ - /* - * We loop to find the bottom base type in a stack of domains. - */ - for (;;) - { - HeapTuple tup; - Form_pg_type typTup; - - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "getBaseTypeMod: failed to lookup type %u", typid); - typTup = (Form_pg_type) GETSTRUCT(tup); - if (typTup->typtype != 'd') - { - /* Not a domain, so done */ - ReleaseSysCache(tup); - break; - } - - /* - * The typmod applied to a domain should always be -1. - * - * We substitute the domain's typmod as we switch attention to the - * base type. - */ - Assert(typmod < 0); - - typid = typTup->typbasetype; - typmod = typTup->typtypmod; - ReleaseSysCache(tup); - } - - return typmod; -} - /* * get_typavgwidth * diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index a3fad0d6834cb6f4ae099ce1b62c2442afab8323..830263bb03c1c18f31d76c3d860d29f1d9ce997a 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -27,7 +27,7 @@ # Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group # Portions Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.173 2002/09/05 19:56:57 tgl Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.174 2002/09/18 21:35:23 tgl Exp $ # #------------------------------------------------------------------------- @@ -1018,7 +1018,7 @@ echo "ok" # Create pg_conversion and support functions $ECHO_N "creating conversions... "$ECHO_C -cat $datadir/conversion_create.sql | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely +grep -v '^DROP CONVERSION' $datadir/conversion_create.sql | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely echo "ok" # Set most system catalogs and built-in functions as world-accessible. @@ -1063,7 +1063,7 @@ UPDATE pg_database SET \ -- We use the OID of template0 to determine lastsysoid UPDATE pg_database SET datlastsysoid = \ - (SELECT oid - 1 FROM pg_database WHERE datname = 'template0'); + (SELECT oid::int4 - 1 FROM pg_database WHERE datname = 'template0'); -- Explicitly revoke public create-schema and create-temp-table privileges -- in template1 and template0; else the latter would be on by default diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 3945c3e600585b29626889353740b05cc46f4aa7..d19fcf461c8c26282f40214262657a5d64775fcc 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.298 2002/09/07 16:14:33 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.299 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3797,7 +3797,7 @@ dumpCasts(Archive *fout, selectSourceSchema("pg_catalog"); if (fout->remoteVersion >= 70300) - appendPQExpBuffer(query, "SELECT oid, castsource, casttarget, castfunc, castimplicit FROM pg_cast ORDER BY 1,2,3;"); + appendPQExpBuffer(query, "SELECT oid, castsource, casttarget, castfunc, castcontext FROM pg_cast ORDER BY 1,2,3;"); else appendPQExpBuffer(query, "SELECT p.oid, t1.oid, t2.oid, p.oid, true FROM pg_type t1, pg_type t2, pg_proc p WHERE p.pronargs = 1 AND p.proargtypes[0] = t1.oid AND p.prorettype = t2.oid AND p.proname = t2.typname ORDER BY 1,2,3;"); @@ -3816,7 +3816,7 @@ dumpCasts(Archive *fout, char *castsource = PQgetvalue(res, i, 1); char *casttarget = PQgetvalue(res, i, 2); char *castfunc = PQgetvalue(res, i, 3); - char *castimplicit = PQgetvalue(res, i, 4); + char *castcontext = PQgetvalue(res, i, 4); int fidx = -1; const char *((*deps)[]); @@ -3859,8 +3859,10 @@ dumpCasts(Archive *fout, appendPQExpBuffer(defqry, "WITH FUNCTION %s", format_function_signature(&finfo[fidx], true)); - if (strcmp(castimplicit, "t") == 0) + if (strcmp(castcontext, "a") == 0) appendPQExpBuffer(defqry, " AS ASSIGNMENT"); + else if (strcmp(castcontext, "i") == 0) + appendPQExpBuffer(defqry, " AS IMPLICIT"); appendPQExpBuffer(defqry, ";\n"); ArchiveEntry(fout, castoid, diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 817ef79bcafd2bf2811fcf35d655cbb3113221b1..21a7732c5fb028ddca3636b16f3bacee4202b682 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.158 2002/09/02 06:24:15 momjian Exp $ + * $Id: catversion.h,v 1.159 2002/09/18 21:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200209021 +#define CATALOG_VERSION_NO 200209181 #endif diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h index 31763358d1c8925698153e1edd6011b5243afc63..48ff930bafb770b7fd5ca34ed95f4bfee1ed0a18 100644 --- a/src/include/catalog/pg_cast.h +++ b/src/include/catalog/pg_cast.h @@ -7,7 +7,7 @@ * * Copyright (c) 2002, PostgreSQL Global Development Group * - * $Id: pg_cast.h,v 1.3 2002/09/04 20:31:37 momjian Exp $ + * $Id: pg_cast.h,v 1.4 2002/09/18 21:35:23 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -22,17 +22,38 @@ CATALOG(pg_cast) { Oid castsource; /* source datatype for cast */ Oid casttarget; /* destination datatype for cast */ - Oid castfunc; /* 0 = binary compatible */ - bool castimplicit; /* allow implicit casting? */ + Oid castfunc; /* cast function; 0 = binary coercible */ + char castcontext; /* contexts in which cast can be used */ } FormData_pg_cast; typedef FormData_pg_cast *Form_pg_cast; +/* + * The allowable values for pg_cast.castcontext are specified by this enum. + * Since castcontext is stored as a "char", we use ASCII codes for human + * convenience in reading the table. Note that internally to the backend, + * these values are converted to the CoercionContext enum (see primnodes.h), + * which is defined to sort in a convenient order; the ASCII codes don't + * have to sort in any special order. + */ + +typedef enum CoercionCodes +{ + COERCION_CODE_IMPLICIT = 'i', /* coercion in context of expression */ + COERCION_CODE_ASSIGNMENT = 'a', /* coercion in context of assignment */ + COERCION_CODE_EXPLICIT = 'e' /* explicit cast operation */ +} CoercionCodes; + + +/* ---------------- + * compiler constants for pg_cast + * ---------------- + */ #define Natts_pg_cast 4 #define Anum_pg_cast_castsource 1 #define Anum_pg_cast_casttarget 2 #define Anum_pg_cast_castfunc 3 -#define Anum_pg_cast_castimplicit 4 +#define Anum_pg_cast_castcontext 4 /* ---------------- * initial contents of pg_cast @@ -40,197 +61,216 @@ typedef FormData_pg_cast *Form_pg_cast; */ /* - * binary compatible casts + * Numeric category: implicit casts are allowed in the direction + * int2->int4->int8->numeric->float4->float8, while casts in the + * reverse direction are assignment-only. */ -DATA(insert ( 25 1042 0 t )); -DATA(insert ( 25 1043 0 t )); -DATA(insert ( 1042 25 0 t )); -DATA(insert ( 1042 1043 0 t )); -DATA(insert ( 1043 25 0 t )); -DATA(insert ( 1043 1042 0 t )); - -DATA(insert ( 23 24 0 t )); -DATA(insert ( 23 26 0 t )); -DATA(insert ( 23 2202 0 t )); -DATA(insert ( 23 2203 0 t )); -DATA(insert ( 23 2204 0 t )); -DATA(insert ( 23 2205 0 t )); -DATA(insert ( 23 2206 0 t )); -DATA(insert ( 24 23 0 t )); -DATA(insert ( 24 26 0 t )); -DATA(insert ( 24 2202 0 t )); -DATA(insert ( 24 2203 0 t )); -DATA(insert ( 24 2204 0 t )); -DATA(insert ( 24 2205 0 t )); -DATA(insert ( 24 2206 0 t )); -DATA(insert ( 26 23 0 t )); -DATA(insert ( 26 24 0 t )); -DATA(insert ( 26 2202 0 t )); -DATA(insert ( 26 2203 0 t )); -DATA(insert ( 26 2204 0 t )); -DATA(insert ( 26 2205 0 t )); -DATA(insert ( 26 2206 0 t )); -DATA(insert ( 2202 23 0 t )); -DATA(insert ( 2202 24 0 t )); -DATA(insert ( 2202 26 0 t )); -DATA(insert ( 2202 2203 0 t )); -DATA(insert ( 2202 2204 0 t )); -DATA(insert ( 2202 2205 0 t )); -DATA(insert ( 2202 2206 0 t )); -DATA(insert ( 2203 23 0 t )); -DATA(insert ( 2203 24 0 t )); -DATA(insert ( 2203 26 0 t )); -DATA(insert ( 2203 2202 0 t )); -DATA(insert ( 2203 2204 0 t )); -DATA(insert ( 2203 2205 0 t )); -DATA(insert ( 2203 2206 0 t )); -DATA(insert ( 2204 23 0 t )); -DATA(insert ( 2204 24 0 t )); -DATA(insert ( 2204 26 0 t )); -DATA(insert ( 2204 2202 0 t )); -DATA(insert ( 2204 2203 0 t )); -DATA(insert ( 2204 2205 0 t )); -DATA(insert ( 2204 2206 0 t )); -DATA(insert ( 2205 23 0 t )); -DATA(insert ( 2205 24 0 t )); -DATA(insert ( 2205 26 0 t )); -DATA(insert ( 2205 2202 0 t )); -DATA(insert ( 2205 2203 0 t )); -DATA(insert ( 2205 2204 0 t )); -DATA(insert ( 2205 2206 0 t )); -DATA(insert ( 2206 23 0 t )); -DATA(insert ( 2206 24 0 t )); -DATA(insert ( 2206 26 0 t )); -DATA(insert ( 2206 2202 0 t )); -DATA(insert ( 2206 2203 0 t )); -DATA(insert ( 2206 2204 0 t )); -DATA(insert ( 2206 2205 0 t )); - -DATA(insert ( 23 702 0 t )); -DATA(insert ( 702 23 0 t )); - -DATA(insert ( 23 703 0 t )); -DATA(insert ( 703 23 0 t )); - -DATA(insert ( 650 869 0 t )); -DATA(insert ( 869 650 0 t )); - -DATA(insert ( 1560 1562 0 t )); -DATA(insert ( 1562 1560 0 t )); +DATA(insert ( 20 21 714 a )); +DATA(insert ( 20 23 480 a )); +DATA(insert ( 20 700 652 i )); +DATA(insert ( 20 701 482 i )); +DATA(insert ( 20 1700 1781 i )); +DATA(insert ( 21 20 754 i )); +DATA(insert ( 21 23 313 i )); +DATA(insert ( 21 700 236 i )); +DATA(insert ( 21 701 235 i )); +DATA(insert ( 21 1700 1782 i )); +DATA(insert ( 23 20 481 i )); +DATA(insert ( 23 21 314 a )); +DATA(insert ( 23 700 318 i )); +DATA(insert ( 23 701 316 i )); +DATA(insert ( 23 1700 1740 i )); +DATA(insert ( 700 20 653 a )); +DATA(insert ( 700 21 238 a )); +DATA(insert ( 700 23 319 a )); +DATA(insert ( 700 701 311 i )); +DATA(insert ( 700 1700 1742 a )); +DATA(insert ( 701 20 483 a )); +DATA(insert ( 701 21 237 a )); +DATA(insert ( 701 23 317 a )); +DATA(insert ( 701 700 312 a )); +DATA(insert ( 701 1700 1743 a )); +DATA(insert ( 1700 20 1779 a )); +DATA(insert ( 1700 21 1783 a )); +DATA(insert ( 1700 23 1744 a )); +DATA(insert ( 1700 700 1745 i )); +DATA(insert ( 1700 701 1746 i )); /* - * regular casts through a function - * - * This list can be obtained from the following query as long as the - * naming convention of the cast functions remains the same: + * OID category: allow implicit conversion from any integral type (including + * int8, to support OID literals > 2G) to OID, as well as assignment coercion + * from OID to int4 or int8. Similarly for each OID-alias type. Also allow + * implicit coercions between OID and each OID-alias type, as well as + * regproc<->regprocedure and regoper<->regoperator. (Other coercions + * between alias types must pass through OID.) + */ +DATA(insert ( 20 26 1287 i )); +DATA(insert ( 21 26 313 i )); +DATA(insert ( 23 26 0 i )); +DATA(insert ( 26 20 1288 a )); +DATA(insert ( 26 23 0 a )); +DATA(insert ( 26 24 0 i )); +DATA(insert ( 24 26 0 i )); +DATA(insert ( 20 24 1287 i )); +DATA(insert ( 21 24 313 i )); +DATA(insert ( 23 24 0 i )); +DATA(insert ( 24 20 1288 a )); +DATA(insert ( 24 23 0 a )); +DATA(insert ( 24 2202 0 i )); +DATA(insert ( 2202 24 0 i )); +DATA(insert ( 26 2202 0 i )); +DATA(insert ( 2202 26 0 i )); +DATA(insert ( 20 2202 1287 i )); +DATA(insert ( 21 2202 313 i )); +DATA(insert ( 23 2202 0 i )); +DATA(insert ( 2202 20 1288 a )); +DATA(insert ( 2202 23 0 a )); +DATA(insert ( 26 2203 0 i )); +DATA(insert ( 2203 26 0 i )); +DATA(insert ( 20 2203 1287 i )); +DATA(insert ( 21 2203 313 i )); +DATA(insert ( 23 2203 0 i )); +DATA(insert ( 2203 20 1288 a )); +DATA(insert ( 2203 23 0 a )); +DATA(insert ( 2203 2204 0 i )); +DATA(insert ( 2204 2203 0 i )); +DATA(insert ( 26 2204 0 i )); +DATA(insert ( 2204 26 0 i )); +DATA(insert ( 20 2204 1287 i )); +DATA(insert ( 21 2204 313 i )); +DATA(insert ( 23 2204 0 i )); +DATA(insert ( 2204 20 1288 a )); +DATA(insert ( 2204 23 0 a )); +DATA(insert ( 26 2205 0 i )); +DATA(insert ( 2205 26 0 i )); +DATA(insert ( 20 2205 1287 i )); +DATA(insert ( 21 2205 313 i )); +DATA(insert ( 23 2205 0 i )); +DATA(insert ( 2205 20 1288 a )); +DATA(insert ( 2205 23 0 a )); +DATA(insert ( 26 2206 0 i )); +DATA(insert ( 2206 26 0 i )); +DATA(insert ( 20 2206 1287 i )); +DATA(insert ( 21 2206 313 i )); +DATA(insert ( 23 2206 0 i )); +DATA(insert ( 2206 20 1288 a )); +DATA(insert ( 2206 23 0 a )); + +/* + * String category: this needs to be tightened up + */ +DATA(insert ( 25 1042 0 i )); +DATA(insert ( 25 1043 0 i )); +DATA(insert ( 1042 25 0 i )); +DATA(insert ( 1042 1043 0 i )); +DATA(insert ( 1043 25 0 i )); +DATA(insert ( 1043 1042 0 i )); +DATA(insert ( 18 25 946 i )); +DATA(insert ( 18 1042 860 i )); +DATA(insert ( 19 25 406 i )); +DATA(insert ( 19 1042 408 i )); +DATA(insert ( 19 1043 1401 i )); +DATA(insert ( 25 18 944 a )); +DATA(insert ( 25 19 407 i )); +DATA(insert ( 1042 19 409 i )); +DATA(insert ( 1043 19 1400 i )); + +/* + * Datetime category + */ +DATA(insert ( 702 1082 1179 a )); +DATA(insert ( 702 1083 1364 a )); +DATA(insert ( 702 1114 2023 i )); +DATA(insert ( 702 1184 1173 i )); +DATA(insert ( 703 1186 1177 i )); +DATA(insert ( 1082 1114 2024 i )); +DATA(insert ( 1082 1184 1174 i )); +DATA(insert ( 1083 1186 1370 i )); +DATA(insert ( 1083 1266 2047 i )); +DATA(insert ( 1114 702 2030 a )); +DATA(insert ( 1114 1082 2029 a )); +DATA(insert ( 1114 1083 1316 a )); +DATA(insert ( 1114 1184 2028 i )); +DATA(insert ( 1184 702 1180 a )); +DATA(insert ( 1184 1082 1178 a )); +DATA(insert ( 1184 1083 2019 a )); +DATA(insert ( 1184 1114 2027 a )); +DATA(insert ( 1184 1266 1388 a )); +DATA(insert ( 1186 703 1194 a )); +DATA(insert ( 1186 1083 1419 a )); +DATA(insert ( 1266 1083 2046 a )); +/* Cross-category casts between int4 and abstime, reltime */ +DATA(insert ( 23 702 0 e )); +DATA(insert ( 702 23 0 e )); +DATA(insert ( 23 703 0 e )); +DATA(insert ( 703 23 0 e )); + +/* + * Geometric category + */ +DATA(insert ( 601 600 1532 e )); +DATA(insert ( 602 600 1533 e )); +DATA(insert ( 602 604 1449 a )); +DATA(insert ( 603 600 1534 e )); +DATA(insert ( 603 601 1541 e )); +DATA(insert ( 603 604 1448 a )); +DATA(insert ( 603 718 1479 e )); +DATA(insert ( 604 600 1540 e )); +DATA(insert ( 604 602 1447 a )); +DATA(insert ( 604 603 1446 e )); +DATA(insert ( 604 718 1474 e )); +DATA(insert ( 718 600 1416 e )); +DATA(insert ( 718 603 1480 e )); +DATA(insert ( 718 604 1544 e )); + +/* + * INET category + */ +DATA(insert ( 650 869 0 i )); +DATA(insert ( 869 650 0 i )); + +/* + * BitString category + */ +DATA(insert ( 1560 1562 0 i )); +DATA(insert ( 1562 1560 0 i )); + +/* + * Cross-category casts to and from TEXT * - * select p.proargtypes[0] as source, p.prorettype as target, p.oid as func, - * p.proimplicit as implicit - * from pg_proc p, pg_type t where p.pronargs=1 and p.proname = t.typname - * and p.prorettype = t.oid order by 1, 2; + * For historical reasons, most casts to TEXT are implicit. This is BAD + * and should be reined in. */ -DATA(insert ( 18 25 946 t )); -DATA(insert ( 18 1042 860 t )); -DATA(insert ( 19 25 406 t )); -DATA(insert ( 19 1042 408 t )); -DATA(insert ( 19 1043 1401 t )); -DATA(insert ( 20 21 714 t )); -DATA(insert ( 20 23 480 t )); -DATA(insert ( 20 25 1288 t )); -DATA(insert ( 20 701 482 t )); -DATA(insert ( 20 1043 1623 f )); -DATA(insert ( 20 1700 1781 t )); -DATA(insert ( 21 20 754 t )); -DATA(insert ( 21 23 313 t )); -DATA(insert ( 21 25 113 t )); -DATA(insert ( 21 700 236 t )); -DATA(insert ( 21 701 235 t )); -DATA(insert ( 21 1700 1782 t )); -DATA(insert ( 23 20 481 t )); -DATA(insert ( 23 21 314 t )); -DATA(insert ( 23 25 112 t )); -DATA(insert ( 23 700 318 t )); -DATA(insert ( 23 701 316 t )); -DATA(insert ( 23 1043 1619 f )); -DATA(insert ( 23 1700 1740 t )); -DATA(insert ( 25 18 944 t )); -DATA(insert ( 25 19 407 t )); -DATA(insert ( 25 20 1289 f )); -DATA(insert ( 25 21 818 f )); -DATA(insert ( 25 23 819 f )); -DATA(insert ( 25 26 817 f )); -DATA(insert ( 25 650 1714 f )); -DATA(insert ( 25 700 839 f )); -DATA(insert ( 25 701 838 f )); -DATA(insert ( 25 829 767 f )); -DATA(insert ( 25 869 1713 f )); -DATA(insert ( 25 1082 748 f )); -DATA(insert ( 25 1083 837 f )); -DATA(insert ( 25 1114 2022 f )); -DATA(insert ( 25 1184 1191 f )); -DATA(insert ( 25 1186 1263 f )); -DATA(insert ( 25 1266 938 f )); -DATA(insert ( 26 25 114 t )); -DATA(insert ( 601 600 1532 f )); -DATA(insert ( 602 600 1533 f )); -DATA(insert ( 602 604 1449 f )); -DATA(insert ( 603 600 1534 f )); -DATA(insert ( 603 601 1541 f )); -DATA(insert ( 603 604 1448 f )); -DATA(insert ( 603 718 1479 f )); -DATA(insert ( 604 600 1540 f )); -DATA(insert ( 604 602 1447 f )); -DATA(insert ( 604 603 1446 f )); -DATA(insert ( 604 718 1474 f )); -DATA(insert ( 700 21 238 f )); -DATA(insert ( 700 23 319 f )); -DATA(insert ( 700 25 841 t )); -DATA(insert ( 700 701 311 t )); -DATA(insert ( 700 1700 1742 t )); -DATA(insert ( 701 20 483 t )); -DATA(insert ( 701 21 237 f )); -DATA(insert ( 701 23 317 f )); -DATA(insert ( 701 25 840 t )); -DATA(insert ( 701 700 312 t )); -DATA(insert ( 701 1700 1743 t )); -DATA(insert ( 702 1082 1179 f )); -DATA(insert ( 702 1083 1364 f )); -DATA(insert ( 702 1114 2023 t )); -DATA(insert ( 702 1184 1173 t )); -DATA(insert ( 703 1186 1177 t )); -DATA(insert ( 718 600 1416 f )); -DATA(insert ( 718 603 1480 f )); -DATA(insert ( 718 604 1544 f )); -DATA(insert ( 829 25 752 f )); -DATA(insert ( 869 25 730 f )); -DATA(insert ( 1042 19 409 t )); -DATA(insert ( 1043 19 1400 t )); -DATA(insert ( 1082 25 749 t )); -DATA(insert ( 1082 1114 2024 t )); -DATA(insert ( 1082 1184 1174 t )); -DATA(insert ( 1083 25 948 t )); -DATA(insert ( 1083 1186 1370 t )); -DATA(insert ( 1083 1266 2047 t )); -DATA(insert ( 1114 25 2034 t )); -DATA(insert ( 1114 702 2030 f )); -DATA(insert ( 1114 1082 2029 f )); -DATA(insert ( 1114 1083 1316 f )); -DATA(insert ( 1114 1184 2028 t )); -DATA(insert ( 1184 25 1192 t )); -DATA(insert ( 1184 702 1180 f )); -DATA(insert ( 1184 1082 1178 f )); -DATA(insert ( 1184 1083 2019 f )); -DATA(insert ( 1184 1114 2027 t )); -DATA(insert ( 1184 1266 1388 f )); -DATA(insert ( 1186 25 1193 t )); -DATA(insert ( 1186 703 1194 f )); -DATA(insert ( 1186 1083 1419 f )); -DATA(insert ( 1266 25 939 t )); -DATA(insert ( 1266 1083 2046 t )); -DATA(insert ( 1700 20 1779 f )); -DATA(insert ( 1700 21 1783 f )); -DATA(insert ( 1700 23 1744 f )); -DATA(insert ( 1700 700 1745 f )); -DATA(insert ( 1700 701 1746 f )); +DATA(insert ( 20 25 1289 i )); +DATA(insert ( 25 20 1290 e )); +DATA(insert ( 21 25 113 i )); +DATA(insert ( 25 21 818 e )); +DATA(insert ( 23 25 112 i )); +DATA(insert ( 25 23 819 e )); +DATA(insert ( 26 25 114 i )); +DATA(insert ( 25 26 817 e )); +DATA(insert ( 25 650 1714 e )); +DATA(insert ( 700 25 841 i )); +DATA(insert ( 25 700 839 e )); +DATA(insert ( 701 25 840 i )); +DATA(insert ( 25 701 838 e )); +DATA(insert ( 829 25 752 e )); +DATA(insert ( 25 829 767 e )); +DATA(insert ( 869 25 730 e )); +DATA(insert ( 25 869 1713 e )); +DATA(insert ( 1082 25 749 i )); +DATA(insert ( 25 1082 748 e )); +DATA(insert ( 1083 25 948 i )); +DATA(insert ( 25 1083 837 e )); +DATA(insert ( 1114 25 2034 i )); +DATA(insert ( 25 1114 2022 e )); +DATA(insert ( 1184 25 1192 i )); +DATA(insert ( 25 1184 1191 e )); +DATA(insert ( 1186 25 1193 i )); +DATA(insert ( 25 1186 1263 e )); +DATA(insert ( 1266 25 939 i )); +DATA(insert ( 25 1266 938 e )); +DATA(insert ( 1700 25 1688 i )); +DATA(insert ( 25 1700 1686 e )); #endif /* PG_CAST_H */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index c770150f7346ece1d4dbb49ebb8cbde0d40ef1c6..c97003cf43150fd585d3218663511ac387ccf9fe 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_operator.h,v 1.109 2002/09/04 20:31:37 momjian Exp $ + * $Id: pg_operator.h,v 1.110 2002/09/18 21:35:23 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -526,10 +526,6 @@ DATA(insert OID = 1133 ( ">" PGNSP PGUID b f 701 700 16 1122 1134 0 0 0 0 f DATA(insert OID = 1134 ( "<=" PGNSP PGUID b f 701 700 16 1125 1133 0 0 0 0 float84le scalarltsel scalarltjoinsel )); DATA(insert OID = 1135 ( ">=" PGNSP PGUID b f 701 700 16 1124 1132 0 0 0 0 float84ge scalargtsel scalargtjoinsel )); -/* int4 vs oid equality --- use oid (unsigned) comparison */ -DATA(insert OID = 1136 ( "=" PGNSP PGUID b t 23 26 16 1137 1656 0 0 0 0 oideq eqsel eqjoinsel )); -DATA(insert OID = 1137 ( "=" PGNSP PGUID b t 26 23 16 1136 1661 0 0 0 0 oideq eqsel eqjoinsel )); - DATA(insert OID = 1158 ( "!" PGNSP PGUID r f 21 0 23 0 0 0 0 0 0 int2fac - - )); DATA(insert OID = 1175 ( "!!" PGNSP PGUID l f 0 21 23 0 0 0 0 0 0 int2fac - - )); @@ -723,17 +719,13 @@ DATA(insert OID = 1631 ( "~~*" PGNSP PGUID b f 1043 25 16 0 1632 0 0 0 0 tex #define OID_VARCHAR_ICLIKE_OP 1631 DATA(insert OID = 1632 ( "!~~*" PGNSP PGUID b f 1043 25 16 0 1631 0 0 0 0 texticnlike icnlikesel icnlikejoinsel )); -/* int4 vs oid comparisons --- use oid (unsigned) comparison */ -DATA(insert OID = 1656 ( "<>" PGNSP PGUID b f 23 26 16 1661 1136 0 0 0 0 oidne neqsel neqjoinsel )); -DATA(insert OID = 1657 ( "<" PGNSP PGUID b f 23 26 16 1663 1660 0 0 0 0 oidlt scalarltsel scalarltjoinsel )); -DATA(insert OID = 1658 ( ">" PGNSP PGUID b f 23 26 16 1662 1659 0 0 0 0 oidgt scalargtsel scalargtjoinsel )); -DATA(insert OID = 1659 ( "<=" PGNSP PGUID b f 23 26 16 1665 1658 0 0 0 0 oidle scalarltsel scalarltjoinsel )); -DATA(insert OID = 1660 ( ">=" PGNSP PGUID b f 23 26 16 1664 1657 0 0 0 0 oidge scalargtsel scalargtjoinsel )); -DATA(insert OID = 1661 ( "<>" PGNSP PGUID b f 26 23 16 1656 1137 0 0 0 0 oidne neqsel neqjoinsel )); -DATA(insert OID = 1662 ( "<" PGNSP PGUID b f 26 23 16 1658 1665 0 0 0 0 oidlt scalarltsel scalarltjoinsel )); -DATA(insert OID = 1663 ( ">" PGNSP PGUID b f 26 23 16 1657 1664 0 0 0 0 oidgt scalargtsel scalargtjoinsel )); -DATA(insert OID = 1664 ( "<=" PGNSP PGUID b f 26 23 16 1660 1663 0 0 0 0 oidle scalarltsel scalarltjoinsel )); -DATA(insert OID = 1665 ( ">=" PGNSP PGUID b f 26 23 16 1659 1662 0 0 0 0 oidge scalargtsel scalargtjoinsel )); +/* regproc comparisons --- use oid (unsigned) comparison */ +DATA(insert OID = 1656 ( "=" PGNSP PGUID b t 24 24 16 1656 1657 1658 1658 1658 1659 oideq eqsel eqjoinsel )); +DATA(insert OID = 1657 ( "<>" PGNSP PGUID b f 24 24 16 1657 1656 0 0 0 0 oidne neqsel neqjoinsel )); +DATA(insert OID = 1658 ( "<" PGNSP PGUID b f 24 24 16 1659 1661 0 0 0 0 oidlt scalarltsel scalarltjoinsel )); +DATA(insert OID = 1659 ( ">" PGNSP PGUID b f 24 24 16 1658 1660 0 0 0 0 oidgt scalargtsel scalargtjoinsel )); +DATA(insert OID = 1660 ( "<=" PGNSP PGUID b f 24 24 16 1661 1659 0 0 0 0 oidle scalarltsel scalarltjoinsel )); +DATA(insert OID = 1661 ( ">=" PGNSP PGUID b f 24 24 16 1660 1658 0 0 0 0 oidge scalargtsel scalargtjoinsel )); /* NUMERIC type - OID's 1700-1799 */ DATA(insert OID = 1751 ( "-" PGNSP PGUID l f 0 1700 1700 0 0 0 0 0 0 numeric_uminus - - )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ff2200d2cc000dd6bef096b659416a2ae4f0a391..eb44f283b9197812b80de05070fb6692b66c3657 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.271 2002/09/12 00:21:24 momjian Exp $ + * $Id: pg_proc.h,v 1.272 2002/09/18 21:35:23 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -886,15 +886,20 @@ DESCR("convert int8 to float8"); DATA(insert OID = 483 ( int8 PGNSP PGUID 12 f f t f i 1 20 "701" dtoi8 - _null_ )); DESCR("convert float8 to int8"); +/* OIDS 500 - 599 */ + +/* OIDS 600 - 699 */ + +DATA(insert OID = 652 ( float4 PGNSP PGUID 12 f f t f i 1 700 "20" i8tof - _null_ )); +DESCR("convert int8 to float4"); +DATA(insert OID = 653 ( int8 PGNSP PGUID 12 f f t f i 1 20 "700" ftoi8 - _null_ )); +DESCR("convert float4 to int8"); + DATA(insert OID = 714 ( int2 PGNSP PGUID 12 f f t f i 1 21 "20" int82 - _null_ )); DESCR("convert int8 to int2"); DATA(insert OID = 754 ( int8 PGNSP PGUID 12 f f t f i 1 20 "21" int28 - _null_ )); DESCR("convert int2 to int8"); -/* OIDS 500 - 599 */ - -/* OIDS 600 - 699 */ - DATA(insert OID = 1285 ( int4notin PGNSP PGUID 12 f f t f s 2 16 "23 25" int4notin - _null_ )); DESCR("not in"); DATA(insert OID = 1286 ( oidnotin PGNSP PGUID 12 f f t f s 2 16 "26 25" oidnotin - _null_ )); @@ -910,9 +915,9 @@ DESCR("greater-than-or-equal"); DATA(insert OID = 659 ( namene PGNSP PGUID 12 f f t f i 2 16 "19 19" namene - _null_ )); DESCR("not equal"); -DATA(insert OID = 668 ( bpchar PGNSP PGUID 12 f f t f i 2 1042 "1042 23" bpchar - _null_ )); +DATA(insert OID = 668 ( bpchar PGNSP PGUID 12 f f t f i 3 1042 "1042 23 16" bpchar - _null_ )); DESCR("adjust char() to typmod length"); -DATA(insert OID = 669 ( varchar PGNSP PGUID 12 f f t f i 2 1043 "1043 23" varchar - _null_ )); +DATA(insert OID = 669 ( varchar PGNSP PGUID 12 f f t f i 3 1043 "1043 23 16" varchar - _null_ )); DESCR("adjust varchar() to typmod length"); DATA(insert OID = 676 ( mktinterval PGNSP PGUID 12 f f t f i 2 704 "702 702" mktinterval - _null_ )); @@ -1374,7 +1379,7 @@ DATA(insert OID = 1141 ( date_pli PGNSP PGUID 12 f f t f i 2 1082 "1082 23" DESCR("add"); DATA(insert OID = 1142 ( date_mii PGNSP PGUID 12 f f t f i 2 1082 "1082 23" date_mii - _null_ )); DESCR("subtract"); -DATA(insert OID = 1143 ( time_in PGNSP PGUID 12 f f t f s 1 1083 "2275" time_in - _null_ )); +DATA(insert OID = 1143 ( time_in PGNSP PGUID 12 f f t f s 3 1083 "2275 26 23" time_in - _null_ )); DESCR("(internal)"); DATA(insert OID = 1144 ( time_out PGNSP PGUID 12 f f t f i 1 2275 "1083" time_out - _null_ )); DESCR("(internal)"); @@ -1390,7 +1395,7 @@ DESCR("multiply"); DATA(insert OID = 1149 ( circle_div_pt PGNSP PGUID 12 f f t f i 2 718 "718 600" circle_div_pt - _null_ )); DESCR("divide"); -DATA(insert OID = 1150 ( timestamptz_in PGNSP PGUID 12 f f t f s 1 1184 "2275" timestamptz_in - _null_ )); +DATA(insert OID = 1150 ( timestamptz_in PGNSP PGUID 12 f f t f s 3 1184 "2275 26 23" timestamptz_in - _null_ )); DESCR("(internal)"); DATA(insert OID = 1151 ( timestamptz_out PGNSP PGUID 12 f f t f s 1 2275 "1184" timestamptz_out - _null_ )); DESCR("(internal)"); @@ -1409,7 +1414,7 @@ DESCR("greater-than"); DATA(insert OID = 1159 ( timezone PGNSP PGUID 12 f f t f s 2 1114 "25 1184" timestamptz_zone - _null_ )); DESCR("timestamp at a specified time zone"); -DATA(insert OID = 1160 ( interval_in PGNSP PGUID 12 f f t f s 1 1186 "2275" interval_in - _null_ )); +DATA(insert OID = 1160 ( interval_in PGNSP PGUID 12 f f t f s 3 1186 "2275 26 23" interval_in - _null_ )); DESCR("(internal)"); DATA(insert OID = 1161 ( interval_out PGNSP PGUID 12 f f t f i 1 2275 "1186" interval_out - _null_ )); DESCR("(internal)"); @@ -1539,15 +1544,18 @@ DESCR("multiply"); DATA(insert OID = 1281 ( int48div PGNSP PGUID 12 f f t f i 2 20 "23 20" int48div - _null_ )); DESCR("divide"); -DATA(insert OID = 1288 ( text PGNSP PGUID 12 f f t f i 1 25 "20" int8_text - _null_ )); +DATA(insert OID = 1287 ( oid PGNSP PGUID 12 f f t f i 1 26 "20" i8tooid - _null_ )); +DESCR("convert int8 to oid"); +DATA(insert OID = 1288 ( int8 PGNSP PGUID 12 f f t f i 1 20 "26" oidtoi8 - _null_ )); +DESCR("convert oid to int8"); + +DATA(insert OID = 1289 ( text PGNSP PGUID 12 f f t f i 1 25 "20" int8_text - _null_ )); DESCR("convert int8 to text"); -DATA(insert OID = 1289 ( int8 PGNSP PGUID 12 f f t f i 1 20 "25" text_int8 - _null_ )); +DATA(insert OID = 1290 ( int8 PGNSP PGUID 12 f f t f i 1 20 "25" text_int8 - _null_ )); DESCR("convert text to int8"); -DATA(insert OID = 1290 ( _bpchar PGNSP PGUID 12 f f t f i 2 1014 "1014 23" _bpchar - _null_ )); -DESCR("adjust char()[] to typmod length"); -DATA(insert OID = 1291 ( _varchar PGNSP PGUID 12 f f t f i 2 1015 "1015 23" _varchar - _null_ )); -DESCR("adjust varchar()[] to typmod length"); +DATA(insert OID = 1291 ( array_length_coerce PGNSP PGUID 12 f f t f i 3 2277 "2277 23 16" array_length_coerce - _null_ )); +DESCR("adjust any array to element typmod length"); DATA(insert OID = 1292 ( tideq PGNSP PGUID 12 f f t f i 2 16 "27 27" tideq - _null_ )); DESCR("equal"); @@ -1594,7 +1602,7 @@ DESCR("SQL92 interval comparison"); DATA(insert OID = 1311 ( overlaps PGNSP PGUID 14 f f f f i 4 16 "1083 1186 1083 1083" "select ($1, ($1 + $2)) overlaps ($3, $4)" - _null_ )); DESCR("SQL92 interval comparison"); -DATA(insert OID = 1312 ( timestamp_in PGNSP PGUID 12 f f t f s 1 1114 "2275" timestamp_in - _null_ )); +DATA(insert OID = 1312 ( timestamp_in PGNSP PGUID 12 f f t f s 3 1114 "2275 26 23" timestamp_in - _null_ )); DESCR("(internal)"); DATA(insert OID = 1313 ( timestamp_out PGNSP PGUID 12 f f t f s 1 2275 "1114" timestamp_out - _null_ )); DESCR("(internal)"); @@ -1644,7 +1652,7 @@ DATA(insert OID = 1349 ( oidvectortypes PGNSP PGUID 12 f f t f s 1 25 "30" oi DESCR("print type names of oidvector field"); -DATA(insert OID = 1350 ( timetz_in PGNSP PGUID 12 f f t f s 1 1266 "2275" timetz_in - _null_ )); +DATA(insert OID = 1350 ( timetz_in PGNSP PGUID 12 f f t f s 3 1266 "2275 26 23" timetz_in - _null_ )); DESCR("(internal)"); DATA(insert OID = 1351 ( timetz_out PGNSP PGUID 12 f f t f i 1 2275 "1266" timetz_out - _null_ )); DESCR("(internal)"); @@ -1983,7 +1991,7 @@ DESCR("# points in path"); DATA(insert OID = 1556 ( npoints PGNSP PGUID 12 f f t f i 1 23 "604" poly_npoints - _null_ )); DESCR("number of points in polygon"); -DATA(insert OID = 1564 ( bit_in PGNSP PGUID 12 f f t f i 1 1560 "2275" bit_in - _null_ )); +DATA(insert OID = 1564 ( bit_in PGNSP PGUID 12 f f t f i 3 1560 "2275 26 23" bit_in - _null_ )); DESCR("(internal)"); DATA(insert OID = 1565 ( bit_out PGNSP PGUID 12 f f t f i 1 2275 "1560" bit_out - _null_ )); DESCR("(internal)"); @@ -2008,7 +2016,7 @@ DESCR("set sequence value"); DATA(insert OID = 1765 ( setval PGNSP PGUID 12 f f t f v 3 20 "25 20 16" setval_and_iscalled - _null_ )); DESCR("set sequence value and iscalled status"); -DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 f f t f i 1 1562 "2275" varbit_in - _null_ )); +DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 f f t f i 3 1562 "2275 26 23" varbit_in - _null_ )); DESCR("(internal)"); DATA(insert OID = 1580 ( varbit_out PGNSP PGUID 12 f f t f i 1 2275 "1562" varbit_out - _null_ )); DESCR("(internal)"); @@ -2060,8 +2068,6 @@ DESCR("PI"); DATA(insert OID = 1618 ( interval_mul PGNSP PGUID 12 f f t f i 2 1186 "1186 701" interval_mul - _null_ )); DESCR("multiply interval"); -DATA(insert OID = 1619 ( varchar PGNSP PGUID 12 f f t f i 1 1043 "23" int4_text - _null_ )); -DESCR("convert int4 to varchar"); DATA(insert OID = 1620 ( ascii PGNSP PGUID 12 f f t f i 1 23 "25" ascii - _null_ )); DESCR("convert first char to int4"); @@ -2070,8 +2076,6 @@ DESCR("convert int4 to char"); DATA(insert OID = 1622 ( repeat PGNSP PGUID 12 f f t f i 2 25 "25 23" repeat - _null_ )); DESCR("replicate string int4 times"); -DATA(insert OID = 1623 ( varchar PGNSP PGUID 12 f f t f i 1 1043 "20" int8_text - _null_ )); -DESCR("convert int8 to varchar"); DATA(insert OID = 1624 ( mul_d_interval PGNSP PGUID 12 f f t f i 2 1186 "701 1186" mul_d_interval - _null_ )); DATA(insert OID = 1633 ( texticlike PGNSP PGUID 12 f f t f i 2 16 "25 25" texticlike - _null_ )); @@ -2133,9 +2137,9 @@ DESCR("replace all occurrences of old_substr with new_substr in string"); DATA(insert OID = 2088 ( split_part PGNSP PGUID 12 f f t f i 3 25 "25 25 23" split_text - _null_ )); DESCR("split string by field_sep and return field_num"); DATA(insert OID = 2089 ( to_hex PGNSP PGUID 12 f f t f i 1 25 "23" to_hex32 - _null_ )); -DESCR("convert int32 number to hex"); +DESCR("convert int4 number to hex"); DATA(insert OID = 2090 ( to_hex PGNSP PGUID 12 f f t f i 1 25 "20" to_hex64 - _null_ )); -DESCR("convert int64 number to hex"); +DESCR("convert int8 number to hex"); /* for character set encoding support */ @@ -2250,14 +2254,10 @@ DESCR("int4 to bitstring"); DATA(insert OID = 1684 ( int4 PGNSP PGUID 12 f f t f i 1 23 "1560" bittoint4 - _null_ )); DESCR("bitstring to int4"); -DATA(insert OID = 1685 ( bit PGNSP PGUID 12 f f t f i 2 1560 "1560 23" bit - _null_ )); +DATA(insert OID = 1685 ( bit PGNSP PGUID 12 f f t f i 3 1560 "1560 23 16" bit - _null_ )); DESCR("adjust bit() to typmod length"); -DATA(insert OID = 1686 ( _bit PGNSP PGUID 12 f f t f i 2 1561 "1561 23" _bit - _null_ )); -DESCR("adjust bit()[] to typmod length"); -DATA(insert OID = 1687 ( varbit PGNSP PGUID 12 f f t f i 2 1562 "1562 23" varbit - _null_ )); +DATA(insert OID = 1687 ( varbit PGNSP PGUID 12 f f t f i 3 1562 "1562 23 16" varbit - _null_ )); DESCR("adjust varbit() to typmod length"); -DATA(insert OID = 1688 ( _varbit PGNSP PGUID 12 f f t f i 2 1563 "1563 23" _varbit - _null_ )); -DESCR("adjust varbit()[] to typmod length"); DATA(insert OID = 1698 ( position PGNSP PGUID 12 f f t f i 2 23 "1560 1560" bitposition - _null_ )); DESCR("return position of sub-bitstring"); @@ -2351,6 +2351,11 @@ DESCR("text to cidr"); DATA(insert OID = 1715 ( set_masklen PGNSP PGUID 12 f f t f i 2 869 "869 23" inet_set_masklen - _null_ )); DESCR("change the netmask of an inet"); +DATA(insert OID = 1686 ( numeric PGNSP PGUID 12 f f t f i 1 1700 "25" text_numeric - _null_ )); +DESCR("(internal)"); +DATA(insert OID = 1688 ( text PGNSP PGUID 12 f f t f i 1 25 "1700" numeric_text - _null_ )); +DESCR("(internal)"); + DATA(insert OID = 1690 ( time_mi_time PGNSP PGUID 12 f f t f i 2 1186 "1083 1083" time_mi_time - _null_ )); DESCR("minus"); diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index f4e69d3d15886377e8a7d53ce206ad0f2d19ef52..e7c9c29413cbd78b19bafa82fb5c4ccfa5cb4984 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: makefuncs.h,v 1.40 2002/09/04 20:31:43 momjian Exp $ + * $Id: makefuncs.h,v 1.41 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include "nodes/parsenodes.h" + extern A_Expr *makeA_Expr(int oper, List *name, Node *lexpr, Node *rexpr); extern A_Expr *makeSimpleA_Expr(int oper, const char *name, @@ -52,7 +53,8 @@ extern Const *makeNullConst(Oid consttype); extern Alias *makeAlias(const char *aliasname, List *colnames); -extern RelabelType *makeRelabelType(Node *arg, Oid rtype, int32 rtypmod); +extern RelabelType *makeRelabelType(Node *arg, Oid rtype, int32 rtypmod, + CoercionForm rformat); extern RangeVar *makeRangeVar(char *schemaname, char *relname); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 0f703e27eccc612e36c6ff2bdacf57478dadb5b3..56e3922ea9f453f0c7fa19c627cd10bd01f0141a 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.206 2002/09/04 20:31:43 momjian Exp $ + * $Id: parsenodes.h,v 1.207 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1632,7 +1632,7 @@ typedef struct CreateCastStmt TypeName *sourcetype; TypeName *targettype; FuncWithArgs *func; - bool implicit; + CoercionContext context; } CreateCastStmt; /* ---------------------- diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 111ed7f8ce8474c6639a74d828dd58476557a909..1d7c5115b64a44fa2057144335cdd2aed09f9e70 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.67 2002/09/04 20:31:44 momjian Exp $ + * $Id: primnodes.h,v 1.68 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -139,6 +139,30 @@ typedef struct RangeVar * ---------------------------------------------------------------- */ +/* + * CoercionContext - distinguishes the allowed set of type casts + * + * NB: ordering of the alternatives is significant; later (larger) values + * allow more casts than earlier ones. + */ +typedef enum CoercionContext +{ + COERCION_IMPLICIT, /* coercion in context of expression */ + COERCION_ASSIGNMENT, /* coercion in context of assignment */ + COERCION_EXPLICIT /* explicit cast operation */ +} CoercionContext; + +/* + * CoercionForm - information showing how to display a function-call node + */ +typedef enum CoercionForm +{ + COERCE_EXPLICIT_CALL, /* display as a function call */ + COERCE_EXPLICIT_CAST, /* display as an explicit cast */ + COERCE_IMPLICIT_CAST, /* implicit cast, so hide it */ + COERCE_DONTCARE /* special case for pathkeys */ +} CoercionForm; + /* * Expr */ @@ -194,6 +218,7 @@ typedef struct Func Oid funcid; /* PG_PROC OID of the function */ Oid funcresulttype; /* PG_TYPE OID of result value */ bool funcretset; /* true if function returns set */ + CoercionForm funcformat; /* how to display this function call */ FunctionCachePtr func_fcache; /* runtime state, or NULL */ } Func; @@ -460,6 +485,7 @@ typedef struct RelabelType Node *arg; /* input expression */ Oid resulttype; /* output type of coercion expression */ int32 resulttypmod; /* output typmod (usually -1) */ + CoercionForm relabelformat; /* how to display this node */ } RelabelType; diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 152ade0e9bd5f92e6eb1bfdfa028bb6b4dd87ebf..61a63cafeb49f414757d8c20bede7bb6694a1086 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * parse_coerce.h - * * Routines for type coercion. * + * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_coerce.h,v 1.46 2002/09/04 20:31:45 momjian Exp $ + * $Id: parse_coerce.h,v 1.47 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,29 +29,31 @@ typedef enum CATEGORY TIMESPAN_TYPE, GEOMETRIC_TYPE, NETWORK_TYPE, - USER_TYPE, - MIXED_TYPE + USER_TYPE } CATEGORY; -extern bool IsBinaryCompatible(Oid type1, Oid type2); +extern bool IsBinaryCoercible(Oid srctype, Oid targettype); extern bool IsPreferredType(CATEGORY category, Oid type); extern CATEGORY TypeCategory(Oid type); -extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, - bool isExplicit); -extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, - Oid targetTypeId, int32 atttypmod, bool isExplicit); -extern Node *coerce_type_typmod(ParseState *pstate, Node *node, - Oid targetTypeId, int32 atttypmod); -extern Node *coerce_type_constraints(ParseState *pstate, Node *arg, - Oid typeId, bool applyTypmod); +extern Node *coerce_to_target_type(Node *expr, Oid exprtype, + Oid targettype, int32 targettypmod, + CoercionContext ccontext, + CoercionForm cformat); +extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, + CoercionContext ccontext); +extern Node *coerce_type(Node *node, Oid inputTypeId, Oid targetTypeId, + CoercionContext ccontext, CoercionForm cformat); +extern Node *coerce_type_constraints(Node *arg, Oid typeId, + CoercionForm cformat); extern Node *coerce_to_boolean(Node *node, const char *constructName); extern Oid select_common_type(List *typeids, const char *context); -extern Node *coerce_to_common_type(ParseState *pstate, Node *node, - Oid targetTypeId, +extern Node *coerce_to_common_type(Node *node, Oid targetTypeId, const char *context); +extern Oid find_typmod_coercion_function(Oid typeId, int *nargs); + #endif /* PARSE_COERCE_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 8fac035fcc62cc6080ee74fdcd703f7d2eb72c22..beb16e2cc8e916cd452a49548014b7af4f5e1448 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_node.h,v 1.31 2002/06/20 20:29:51 momjian Exp $ + * $Id: parse_node.h,v 1.32 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -57,6 +57,7 @@ extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno); extern ArrayRef *transformArraySubscripts(ParseState *pstate, Node *arrayBase, Oid arrayType, + int32 arrayTypMod, List *indirection, bool forceSlice, Node *assignFrom); diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h index acca0f05690fa35a625fb829b3162f6b5851f76e..b8c495484bfd1ac13f184905b8f6073291d2e985 100644 --- a/src/include/parser/parse_target.h +++ b/src/include/parser/parse_target.h @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * parse_target.h - * + * handle target lists * * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_target.h,v 1.26 2002/09/04 20:31:45 momjian Exp $ + * $Id: parse_target.h,v 1.27 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include "parser/parse_node.h" + extern List *transformTargetList(ParseState *pstate, List *targetlist); extern TargetEntry *transformTargetEntry(ParseState *pstate, Node *node, Node *expr, @@ -23,9 +24,6 @@ extern TargetEntry *transformTargetEntry(ParseState *pstate, extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle, char *colname, int attrno, List *indirection); -extern Node *CoerceTargetExpr(ParseState *pstate, Node *expr, - Oid type_id, Oid attrtype, int32 attrtypmod, - bool isExplicit); extern List *checkInsertTargets(ParseState *pstate, List *cols, List **attrnos); diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 5c1276467c083df01643421318c218b30c2baead..639d9dc31535b0eb16473cdb185815d3acc433ee 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: array.h,v 1.34 2002/09/04 20:31:45 momjian Exp $ + * $Id: array.h,v 1.35 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -82,6 +82,7 @@ typedef struct */ extern Datum array_in(PG_FUNCTION_ARGS); extern Datum array_out(PG_FUNCTION_ARGS); +extern Datum array_length_coerce(PG_FUNCTION_ARGS); extern Datum array_eq(PG_FUNCTION_ARGS); extern Datum array_dims(PG_FUNCTION_ARGS); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index e97982215d84df14bd42524083315186d88603ea..d9f611bcfd8dc9ee9b14bb94c095147ccf657899 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.199 2002/09/04 20:31:45 momjian Exp $ + * $Id: builtins.h,v 1.200 2002/09/18 21:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -422,7 +422,6 @@ extern Datum currtid_byrelname(PG_FUNCTION_ARGS); extern Datum bpcharin(PG_FUNCTION_ARGS); extern Datum bpcharout(PG_FUNCTION_ARGS); extern Datum bpchar(PG_FUNCTION_ARGS); -extern Datum _bpchar(PG_FUNCTION_ARGS); extern Datum char_bpchar(PG_FUNCTION_ARGS); extern Datum name_bpchar(PG_FUNCTION_ARGS); extern Datum bpchar_name(PG_FUNCTION_ARGS); @@ -440,7 +439,6 @@ extern Datum hashbpchar(PG_FUNCTION_ARGS); extern Datum varcharin(PG_FUNCTION_ARGS); extern Datum varcharout(PG_FUNCTION_ARGS); extern Datum varchar(PG_FUNCTION_ARGS); -extern Datum _varchar(PG_FUNCTION_ARGS); extern Datum varchareq(PG_FUNCTION_ARGS); extern Datum varcharne(PG_FUNCTION_ARGS); extern Datum varcharlt(PG_FUNCTION_ARGS); @@ -633,6 +631,8 @@ extern Datum numeric_float8(PG_FUNCTION_ARGS); extern Datum numeric_float8_no_overflow(PG_FUNCTION_ARGS); extern Datum float4_numeric(PG_FUNCTION_ARGS); extern Datum numeric_float4(PG_FUNCTION_ARGS); +extern Datum text_numeric(PG_FUNCTION_ARGS); +extern Datum numeric_text(PG_FUNCTION_ARGS); extern Datum numeric_accum(PG_FUNCTION_ARGS); extern Datum int2_accum(PG_FUNCTION_ARGS); extern Datum int4_accum(PG_FUNCTION_ARGS); diff --git a/src/include/utils/int8.h b/src/include/utils/int8.h index 4ffa3bca50485a464d6ffc56ff3371c7db3de570..f8b337e3c0c31626873025110daf2adf61e91a1d 100644 --- a/src/include/utils/int8.h +++ b/src/include/utils/int8.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: int8.h,v 1.34 2002/06/20 20:29:53 momjian Exp $ + * $Id: int8.h,v 1.35 2002/09/18 21:35:25 tgl Exp $ * * NOTES * These data types are supported on all 64-bit architectures, and may @@ -29,6 +29,8 @@ #define INT64_FORMAT "%ld" #endif +extern bool scanint8(const char *str, bool errorOK, int64 *result); + extern Datum int8in(PG_FUNCTION_ARGS); extern Datum int8out(PG_FUNCTION_ARGS); @@ -106,6 +108,12 @@ extern Datum int82(PG_FUNCTION_ARGS); extern Datum i8tod(PG_FUNCTION_ARGS); extern Datum dtoi8(PG_FUNCTION_ARGS); +extern Datum i8tof(PG_FUNCTION_ARGS); +extern Datum ftoi8(PG_FUNCTION_ARGS); + +extern Datum i8tooid(PG_FUNCTION_ARGS); +extern Datum oidtoi8(PG_FUNCTION_ARGS); + extern Datum int8_text(PG_FUNCTION_ARGS); extern Datum text_int8(PG_FUNCTION_ARGS); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 029fdcdc8638ea6b1e071cd66dc453fc55bfd8e6..5dee7bc0cbb8bdd3b0e2f96c00894ffa58edc117 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.62 2002/09/04 20:31:45 momjian Exp $ + * $Id: lsyscache.h,v 1.63 2002/09/18 21:35:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -58,7 +58,6 @@ extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typElem); extern bool getTypeOutputInfo(Oid type, Oid *typOutput, Oid *typElem, bool *typIsVarlena); extern Oid getBaseType(Oid typid); -extern int32 getBaseTypeMod(Oid typid, int32 typmod); extern int32 get_typavgwidth(Oid typid, int32 typmod); extern int32 get_attavgwidth(Oid relid, AttrNumber attnum); extern bool get_attstatsslot(HeapTuple statstuple, diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h index 8dff3166d566b48c56f7441b363e66279fd1e6bb..9e5505624b6aea837fcef87d51900110bb140fb9 100644 --- a/src/include/utils/varbit.h +++ b/src/include/utils/varbit.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: varbit.h,v 1.15 2002/08/04 06:33:56 thomas Exp $ + * $Id: varbit.h,v 1.16 2002/09/18 21:35:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -66,9 +66,7 @@ extern Datum bit_out(PG_FUNCTION_ARGS); extern Datum varbit_in(PG_FUNCTION_ARGS); extern Datum varbit_out(PG_FUNCTION_ARGS); extern Datum bit(PG_FUNCTION_ARGS); -extern Datum _bit(PG_FUNCTION_ARGS); extern Datum varbit(PG_FUNCTION_ARGS); -extern Datum _varbit(PG_FUNCTION_ARGS); extern Datum biteq(PG_FUNCTION_ARGS); extern Datum bitne(PG_FUNCTION_ARGS); extern Datum bitlt(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 522c28121ff27ef071895d5b017394ac133732f8..8bbed8c431ddf2e48aaf7fec82988f9004c09ce1 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -13,10 +13,14 @@ create domain domainvarchar varchar(5); create domain domainnumeric numeric(8,2); create domain domainint4 int4; create domain domaintext text; --- Test coercions -SELECT cast('123456' as domainvarchar); -- fail -ERROR: value too long for type character varying(5) -SELECT cast('12345' as domainvarchar); -- pass +-- Test explicit coercions --- these should succeed (and truncate) +SELECT cast('123456' as domainvarchar); + domainvarchar +--------------- + 12345 +(1 row) + +SELECT cast('12345' as domainvarchar); domainvarchar --------------- 12345 diff --git a/src/test/regress/expected/horology-no-DST-before-1970.out b/src/test/regress/expected/horology-no-DST-before-1970.out index 3561fac04bf1fec0496d3392114f0b079e3f8c45..b8b7423ec520f01c286b2f5a72b5758255e2279f 100644 --- a/src/test/regress/expected/horology-no-DST-before-1970.out +++ b/src/test/regress/expected/horology-no-DST-before-1970.out @@ -354,12 +354,16 @@ SELECT date '1994-01-01' + time '10:00' AS "Jan_01_1994_10am"; Sat Jan 01 10:00:00 1994 (1 row) -SELECT date '1994-01-01' + time '11:00-5' AS "Jan_01_1994_8am"; -ERROR: Bad time external representation '11:00-5' -SELECT "timestamp"(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_11am"; - Jan_01_1994_11am --------------------------- - Sat Jan 01 11:00:00 1994 +SELECT date '1994-01-01' + timetz '11:00-5' AS "Jan_01_1994_8am"; + Jan_01_1994_8am +------------------------------ + Sat Jan 01 08:00:00 1994 PST +(1 row) + +SELECT timestamptz(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_8am"; + Jan_01_1994_8am +------------------------------ + Sat Jan 01 08:00:00 1994 PST (1 row) SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMP_TBL; @@ -762,9 +766,9 @@ SELECT interval '04:30' - time '01:02' AS "20:32:00"; (1 row) SELECT CAST(time with time zone '01:02-08' AS interval) AS "+00:01"; -ERROR: Cannot cast type 'time with time zone' to 'interval' +ERROR: Cannot cast type time with time zone to interval SELECT CAST(interval '02:03' AS time with time zone) AS "02:03:00-08"; -ERROR: Cannot cast type 'interval' to 'time with time zone' +ERROR: Cannot cast type interval to time with time zone SELECT time with time zone '01:30-08' - interval '02:01' AS "23:29:00-08"; 23:29:00-08 ------------- diff --git a/src/test/regress/expected/horology-solaris-1947.out b/src/test/regress/expected/horology-solaris-1947.out index 22d335c6c2c19eb60b6d9ee6d905c048d9ce3675..1601a346f01612f152b18e3568be3f10fcf7fd0e 100644 --- a/src/test/regress/expected/horology-solaris-1947.out +++ b/src/test/regress/expected/horology-solaris-1947.out @@ -354,12 +354,16 @@ SELECT date '1994-01-01' + time '10:00' AS "Jan_01_1994_10am"; Sat Jan 01 10:00:00 1994 (1 row) -SELECT date '1994-01-01' + time '11:00-5' AS "Jan_01_1994_8am"; -ERROR: Bad time external representation '11:00-5' -SELECT "timestamp"(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_11am"; - Jan_01_1994_11am --------------------------- - Sat Jan 01 11:00:00 1994 +SELECT date '1994-01-01' + timetz '11:00-5' AS "Jan_01_1994_8am"; + Jan_01_1994_8am +------------------------------ + Sat Jan 01 08:00:00 1994 PST +(1 row) + +SELECT timestamptz(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_8am"; + Jan_01_1994_8am +------------------------------ + Sat Jan 01 08:00:00 1994 PST (1 row) SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMP_TBL; @@ -762,9 +766,9 @@ SELECT interval '04:30' - time '01:02' AS "20:32:00"; (1 row) SELECT CAST(time with time zone '01:02-08' AS interval) AS "+00:01"; -ERROR: Cannot cast type 'time with time zone' to 'interval' +ERROR: Cannot cast type time with time zone to interval SELECT CAST(interval '02:03' AS time with time zone) AS "02:03:00-08"; -ERROR: Cannot cast type 'interval' to 'time with time zone' +ERROR: Cannot cast type interval to time with time zone SELECT time with time zone '01:30-08' - interval '02:01' AS "23:29:00-08"; 23:29:00-08 ------------- diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out index 83d92182d085726ba638f8f5990c92e1c9b0f678..294b7854106ba88388d0c70a8f92e559e4406447 100644 --- a/src/test/regress/expected/horology.out +++ b/src/test/regress/expected/horology.out @@ -354,12 +354,16 @@ SELECT date '1994-01-01' + time '10:00' AS "Jan_01_1994_10am"; Sat Jan 01 10:00:00 1994 (1 row) -SELECT date '1994-01-01' + time '11:00-5' AS "Jan_01_1994_8am"; -ERROR: Bad time external representation '11:00-5' -SELECT "timestamp"(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_11am"; - Jan_01_1994_11am --------------------------- - Sat Jan 01 11:00:00 1994 +SELECT date '1994-01-01' + timetz '11:00-5' AS "Jan_01_1994_8am"; + Jan_01_1994_8am +------------------------------ + Sat Jan 01 08:00:00 1994 PST +(1 row) + +SELECT timestamptz(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_8am"; + Jan_01_1994_8am +------------------------------ + Sat Jan 01 08:00:00 1994 PST (1 row) SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMP_TBL; @@ -762,9 +766,9 @@ SELECT interval '04:30' - time '01:02' AS "20:32:00"; (1 row) SELECT CAST(time with time zone '01:02-08' AS interval) AS "+00:01"; -ERROR: Cannot cast type 'time with time zone' to 'interval' +ERROR: Cannot cast type time with time zone to interval SELECT CAST(interval '02:03' AS time with time zone) AS "02:03:00-08"; -ERROR: Cannot cast type 'interval' to 'time with time zone' +ERROR: Cannot cast type interval to time with time zone SELECT time with time zone '01:30-08' - interval '02:01' AS "23:29:00-08"; 23:29:00-08 ------------- diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index c4d26254d4ab2a9721a20132641b1842652fd8b1..3decbdb7991dfa01557f019eb5cbb413d80adc46 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -202,33 +202,35 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT -- **************** pg_cast **************** -- Look for casts from and to the same type. This is not harmful, but --- useless. +-- useless. Also catch bogus values in pg_cast columns (other than +-- cases detected by oidjoins test). SELECT * FROM pg_cast c -WHERE c.castsource = c.casttarget; - castsource | casttarget | castfunc | castimplicit -------------+------------+----------+-------------- +WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0 + OR castcontext NOT IN ('e', 'a', 'i'); + castsource | casttarget | castfunc | castcontext +------------+------------+----------+------------- (0 rows) --- Look for cast functions with incorrect number or type of argument --- or return value. +-- 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 +-- compatible with, what it says in pg_cast. SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND - (p.pronargs <> 1 OR - p.proargtypes[0] <> c.castsource OR - p.prorettype <> c.casttarget); - castsource | casttarget | castfunc | castimplicit -------------+------------+----------+-------------- -(0 rows) - --- Look for binary compatible casts that are not implicit. This is --- legal, but probably not intended. -SELECT * -FROM pg_cast c -WHERE c.castfunc = 0 AND NOT c.castimplicit; - castsource | casttarget | castfunc | castimplicit -------------+------------+----------+-------------- + (p.pronargs <> 1 + OR NOT (c.castsource = p.proargtypes[0] OR + EXISTS (SELECT 1 FROM pg_cast k + WHERE k.castfunc = 0 AND + k.castsource = c.castsource AND + k.casttarget = p.proargtypes[0])) + OR NOT (p.prorettype = c.casttarget OR + EXISTS (SELECT 1 FROM pg_cast k + WHERE k.castfunc = 0 AND + k.castsource = p.prorettype AND + k.casttarget = c.casttarget))); + castsource | casttarget | castfunc | castcontext +------------+------------+----------+------------- (0 rows) -- Look for binary compatible casts that do not have the reverse @@ -241,8 +243,8 @@ WHERE c.castfunc = 0 AND WHERE k.castfunc = 0 AND k.castsource = c.casttarget AND k.casttarget = c.castsource); - castsource | casttarget | castfunc | castimplicit -------------+------------+----------+-------------- + castsource | casttarget | castfunc | castcontext +------------+------------+----------+------------- (0 rows) -- **************** pg_operator **************** @@ -414,20 +416,18 @@ WHERE p1.oprlsortop != p1.oprrsortop AND -- Hashing only works on simple equality operators "type = sametype", -- since the hash itself depends on the bitwise representation of the type. -- Check that allegedly hashable operators look like they might be "=". --- NOTE: in 7.2, this search finds int4eqoid, oideqint4, and xideqint4. +-- NOTE: in 7.3, this search finds xideqint4. -- Until we have some cleaner way of dealing with binary-equivalent types, --- just leave those three tuples in the expected output. +-- just leave that tuple in the expected output. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 WHERE p1.oprcanhash AND NOT (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND p1.oprleft = p1.oprright AND p1.oprname = '=' AND p1.oprcom = p1.oid); - oid | oprname -------+--------- - 353 | = - 1136 | = - 1137 | = -(3 rows) + oid | oprname +-----+--------- + 353 | = +(1 row) -- In 6.5 we accepted hashable array equality operators when the array element -- type is hashable. However, what we actually need to make hashjoin work on diff --git a/src/test/regress/expected/prepare.out b/src/test/regress/expected/prepare.out index 166be86eefd4a40562bbb494f7e645866ff5889e..629e444fb7b7fbce78823dc9ac5865dfb78744dd 100644 --- a/src/test/regress/expected/prepare.out +++ b/src/test/regress/expected/prepare.out @@ -75,7 +75,7 @@ EXECUTE q3('bytea', 5::smallint, 10.5::float, false, 500::oid, 4::bigint, true); ERROR: Wrong number of parameters, expected 6 but got 7 -- wrong param types EXECUTE q3(5::smallint, 10.5::float, false, 500::oid, 4::bigint, 'bytea'); -ERROR: Parameter $2 of type double precision cannot be coerced into the expected type integer +ERROR: Parameter $3 of type boolean cannot be coerced into the expected type double precision You will need to rewrite or cast the expression -- invalid type PREPARE q4(nonexistenttype) AS SELECT $1; diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 6ffa0c3dcd7770511aeec91b3fa21c8fbd2bf769..a4dfd78cac364ba93ca489b86cea2ae96dc22706 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1321,10 +1321,10 @@ SELECT tablename, rulename, definition FROM pg_rules rtest_nothn1 | rtest_nothn_r2 | CREATE RULE rtest_nothn_r2 AS ON INSERT TO rtest_nothn1 WHERE ((new.a >= 30) AND (new.a < 40)) DO INSTEAD NOTHING; rtest_nothn2 | rtest_nothn_r3 | CREATE RULE rtest_nothn_r3 AS ON INSERT TO rtest_nothn2 WHERE (new.a >= 100) DO INSTEAD INSERT INTO rtest_nothn3 (a, b) VALUES (new.a, new.b); rtest_nothn2 | rtest_nothn_r4 | CREATE RULE rtest_nothn_r4 AS ON INSERT TO rtest_nothn2 DO INSTEAD NOTHING; - rtest_order1 | rtest_order_r1 | CREATE RULE rtest_order_r1 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, int4(nextval('rtest_seq'::text)), 'rule 1 - this should run 3rd or 4th'::text); - rtest_order1 | rtest_order_r2 | CREATE RULE rtest_order_r2 AS ON INSERT TO rtest_order1 DO INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, int4(nextval('rtest_seq'::text)), 'rule 2 - this should run 1st'::text); - rtest_order1 | rtest_order_r3 | CREATE RULE rtest_order_r3 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, int4(nextval('rtest_seq'::text)), 'rule 3 - this should run 3rd or 4th'::text); - rtest_order1 | rtest_order_r4 | CREATE RULE rtest_order_r4 AS ON INSERT TO rtest_order1 WHERE (new.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, int4(nextval('rtest_seq'::text)), 'rule 4 - this should run 2nd'::text); + rtest_order1 | rtest_order_r1 | CREATE RULE rtest_order_r1 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 1 - this should run 3rd or 4th'::text); + rtest_order1 | rtest_order_r2 | CREATE RULE rtest_order_r2 AS ON INSERT TO rtest_order1 DO INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 2 - this should run 1st'::text); + rtest_order1 | rtest_order_r3 | CREATE RULE rtest_order_r3 AS ON INSERT TO rtest_order1 DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 3 - this should run 3rd or 4th'::text); + rtest_order1 | rtest_order_r4 | CREATE RULE rtest_order_r4 AS ON INSERT TO rtest_order1 WHERE (new.a < 100) DO INSTEAD INSERT INTO rtest_order2 (a, b, c) VALUES (new.a, nextval('rtest_seq'::text), 'rule 4 - this should run 2nd'::text); rtest_person | rtest_pers_del | CREATE RULE rtest_pers_del AS ON DELETE TO rtest_person DO DELETE FROM rtest_admin WHERE (rtest_admin.pname = old.pname); rtest_person | rtest_pers_upd | CREATE RULE rtest_pers_upd AS ON UPDATE TO rtest_person DO UPDATE rtest_admin SET pname = new.pname WHERE (rtest_admin.pname = old.pname); rtest_system | rtest_sys_del | CREATE RULE rtest_sys_del AS ON DELETE TO rtest_system DO (DELETE FROM rtest_interface WHERE (rtest_interface.sysname = old.sysname); DELETE FROM rtest_admin WHERE (rtest_admin.sysname = old.sysname); ); diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out index ce81969936c5e1a9355e50d0dbc9ba653200e3b0..576fafb7729493308ae297abe0b2f7254d75868f 100644 --- a/src/test/regress/expected/strings.out +++ b/src/test/regress/expected/strings.out @@ -47,8 +47,15 @@ SELECT CAST(name 'namefield' AS text) AS "text(name)"; namefield (1 row) -SELECT CAST(f1 AS char(10)) AS "char(text)" FROM TEXT_TBL; -- fail -ERROR: value too long for type character(10) +-- since this is an explicit cast, it should truncate w/o error: +SELECT CAST(f1 AS char(10)) AS "char(text)" FROM TEXT_TBL; + char(text) +------------ + doh! + hi de ho n +(2 rows) + +-- note: implicit-cast case is tested in char.sql SELECT CAST(f1 AS char(20)) AS "char(text)" FROM TEXT_TBL; char(text) ---------------------- diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out index 0adad8c119d314a4572789ba47287651fdade9ee..a46a44becab4b2a76792d5871373fe02aeea051e 100644 --- a/src/test/regress/expected/union.out +++ b/src/test/regress/expected/union.out @@ -90,7 +90,7 @@ SELECT 1.1 AS two UNION ALL SELECT 2; SELECT 1.0 AS two UNION ALL SELECT 1; two ----- - 1 + 1.0 1 (2 rows) diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 9627870934fe16e533adb5c3f7d3bc86288e551c..a5690694cad3cb431e4f33642a2eaaa0778f3d16 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -20,9 +20,9 @@ create domain domainnumeric numeric(8,2); create domain domainint4 int4; create domain domaintext text; --- Test coercions -SELECT cast('123456' as domainvarchar); -- fail -SELECT cast('12345' as domainvarchar); -- pass +-- Test explicit coercions --- these should succeed (and truncate) +SELECT cast('123456' as domainvarchar); +SELECT cast('12345' as domainvarchar); -- Test tables using domains create table basictest diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql index 136e6c6c6517c046afe3b27bec4d9660b4084593..6d767d23760a57816e29d1bd4bbeb7a01082078a 100644 --- a/src/test/regress/sql/horology.sql +++ b/src/test/regress/sql/horology.sql @@ -90,8 +90,8 @@ SELECT (timestamp without time zone 'tomorrow' > 'now') as "True"; -- to enable support for SQL99 timestamp type syntax. SELECT date '1994-01-01' + time '11:00' AS "Jan_01_1994_11am"; SELECT date '1994-01-01' + time '10:00' AS "Jan_01_1994_10am"; -SELECT date '1994-01-01' + time '11:00-5' AS "Jan_01_1994_8am"; -SELECT "timestamp"(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_11am"; +SELECT date '1994-01-01' + timetz '11:00-5' AS "Jan_01_1994_8am"; +SELECT timestamptz(date '1994-01-01', time with time zone '11:00-5') AS "Jan_01_1994_8am"; SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMP_TBL; SELECT '' AS "64", d1 - interval '1 year' AS one_year FROM TIMESTAMP_TBL; diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 9c3a93e30bea871dc8e9c898d727a4cc20f7bdc0..faacc83850e8c7f673e054387d3ea811c2abab28 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -162,28 +162,32 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT -- **************** pg_cast **************** -- Look for casts from and to the same type. This is not harmful, but --- useless. +-- useless. Also catch bogus values in pg_cast columns (other than +-- cases detected by oidjoins test). SELECT * FROM pg_cast c -WHERE c.castsource = c.casttarget; +WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0 + OR castcontext NOT IN ('e', 'a', 'i'); --- Look for cast functions with incorrect number or type of argument --- or return value. +-- 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 +-- compatible with, what it says in pg_cast. SELECT c.* FROM pg_cast c, pg_proc p WHERE c.castfunc = p.oid AND - (p.pronargs <> 1 OR - p.proargtypes[0] <> c.castsource OR - p.prorettype <> c.casttarget); - --- Look for binary compatible casts that are not implicit. This is --- legal, but probably not intended. - -SELECT * -FROM pg_cast c -WHERE c.castfunc = 0 AND NOT c.castimplicit; + (p.pronargs <> 1 + OR NOT (c.castsource = p.proargtypes[0] OR + EXISTS (SELECT 1 FROM pg_cast k + WHERE k.castfunc = 0 AND + k.castsource = c.castsource AND + k.casttarget = p.proargtypes[0])) + OR NOT (p.prorettype = c.casttarget OR + EXISTS (SELECT 1 FROM pg_cast k + WHERE k.castfunc = 0 AND + k.castsource = p.prorettype AND + k.casttarget = c.casttarget))); -- Look for binary compatible casts that do not have the reverse -- direction registered as well, or where the reverse direction is not @@ -341,9 +345,9 @@ WHERE p1.oprlsortop != p1.oprrsortop AND -- Hashing only works on simple equality operators "type = sametype", -- since the hash itself depends on the bitwise representation of the type. -- Check that allegedly hashable operators look like they might be "=". --- NOTE: in 7.2, this search finds int4eqoid, oideqint4, and xideqint4. +-- NOTE: in 7.3, this search finds xideqint4. -- Until we have some cleaner way of dealing with binary-equivalent types, --- just leave those three tuples in the expected output. +-- just leave that tuple in the expected output. SELECT p1.oid, p1.oprname FROM pg_operator AS p1 diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql index 692233793dc74c7996601d8ac14149b6e3ac9b32..e5c15bc528fdfd516d12dea5821a4703cb2ed081 100644 --- a/src/test/regress/sql/strings.sql +++ b/src/test/regress/sql/strings.sql @@ -27,7 +27,9 @@ SELECT CAST(f1 AS text) AS "text(varchar)" FROM VARCHAR_TBL; SELECT CAST(name 'namefield' AS text) AS "text(name)"; -SELECT CAST(f1 AS char(10)) AS "char(text)" FROM TEXT_TBL; -- fail +-- since this is an explicit cast, it should truncate w/o error: +SELECT CAST(f1 AS char(10)) AS "char(text)" FROM TEXT_TBL; +-- note: implicit-cast case is tested in char.sql SELECT CAST(f1 AS char(20)) AS "char(text)" FROM TEXT_TBL;