Thread: BUG #1532: typecast problem between arrays of an int8 derived datatype and varchar[]

BUG #1532: typecast problem between arrays of an int8 derived datatype and varchar[]

From
"Ezequiel Tolnay"
Date:
The following bug has been logged online:

Bug reference:      1532
Logged by:          Ezequiel Tolnay
Email address:      mail@etolnay.com.ar
PostgreSQL version: 8.0.1
Operating system:   Windows 2003 Server
Description:        typecast problem between arrays of an int8 derived
datatype and varchar[]
Details:

I've created the cardnumber_t datatype, which is an int8, to provide
implicit typecasting with varchar padding the result with zeroes.
Conversions work as expected between int4, int8, cardnumber_t and varchar.
They also work fine between int4[], int8[] and cardnumber_t[], but when an
attempt is made to convert a cardnumber_t[] to a varchar[], the connection
is dropped.

The code used to create the cardnumber_t is the following:

CREATE OR REPLACE FUNCTION cardnumber_t_in(cstring) RETURNS cardnumber_t AS
'int8in'
LANGUAGE INTERNAL IMMUTABLE WITH (isstrict);

CREATE OR REPLACE FUNCTION cardnumber_t_out(cardnumber_t) RETURNS cstring AS
'int8out'
LANGUAGE INTERNAL IMMUTABLE WITH (isstrict);

CREATE TYPE cardnumber_t (
  INTERNALLENGTH = 8,
  INPUT = cardnumber_t_in,
  OUTPUT = cardnumber_t_out,
  STORAGE = plain,
  ALIGNMENT = double
);

CREATE OR REPLACE FUNCTION to_int8(cardnumber_t) RETURNS int8 AS 'int8up'
LANGUAGE INTERNAL IMMUTABLE WITH (isstrict);

CREATE CAST (cardnumber_t AS int8) WITH FUNCTION to_int8(cardnumber_t) AS
IMPLICIT;

CREATE OR REPLACE FUNCTION to_cardnumber_t(int8) RETURNS cardnumber_t AS
'int8up'
LANGUAGE INTERNAL IMMUTABLE WITH (isstrict);

CREATE CAST (int8 AS cardnumber_t) WITH FUNCTION to_cardnumber_t(int8) AS
IMPLICIT;

CREATE OR REPLACE FUNCTION to_cardnumber_t(int4) RETURNS cardnumber_t AS
'int48'
LANGUAGE INTERNAL IMMUTABLE WITH (isstrict);

CREATE CAST (int4 AS cardnumber_t) WITH FUNCTION to_cardnumber_t(int4) AS
IMPLICIT;

CREATE DOMAIN cardnumber AS cardnumber_t CONSTRAINT ch_cardnumber_range
CHECK (VALUE between 1 AND 9999999999999999);

CREATE OR REPLACE FUNCTION fc_cardnumber_t_to_varchar (cn cardnumber_t)
RETURNS varchar AS $$
BEGIN
  RETURN substring((10000000000000000::int8+cn)::varchar, 2, 16);
END; $$ LANGUAGE plpgsql;

CREATE CAST (cardnumber_t as varchar) WITH FUNCTION
fc_cardnumber_t_to_varchar(cardnumber_t) AS IMPLICIT;

The following are successful typecast tests:
SELECT 10::int4::int8::cardnumber_t::varchar
SELECT ((ARRAY[1,2,3])::int8[])::cardnumber_t[]

The following fails and drops the connection
SELECT ((ARRAY[1,2,3])::cardnumber_t[])::varchar[]
"Ezequiel Tolnay" <mail@etolnay.com.ar> writes:
> I've created the cardnumber_t datatype, which is an int8, to provide
> implicit typecasting with varchar padding the result with zeroes.
> Conversions work as expected between int4, int8, cardnumber_t and varchar.
> They also work fine between int4[], int8[] and cardnumber_t[], but when an
> attempt is made to convert a cardnumber_t[] to a varchar[], the connection
> is dropped.

What's going on here is that array_map thinks it can use fn_extra of the
passed FmgrInfo for its own purposes.  That means that if the function
to be called tries to use fn_extra for *its* own purposes, we have a
conflict that is going to lead to core dumps in most cases.  In other
words, array_map pretty much doesn't work for calling anything except
built-in functions.

I think the best solution to this is to require array_map's caller to
provide the state storage array_map wants, instead of doing it locally.
Both of the existing callers can easily incorporate array_map's state
data into their own state structs.  Joe, you have any better ideas?

            regards, tom lane

Re: BUG #1532: typecast problem between arrays of an int8

From
Joe Conway
Date:
Tom Lane wrote:
> "Ezequiel Tolnay" <mail@etolnay.com.ar> writes:
>>I've created the cardnumber_t datatype, which is an int8, to provide
>>implicit typecasting with varchar padding the result with zeroes.
>>Conversions work as expected between int4, int8, cardnumber_t and varchar.
>>They also work fine between int4[], int8[] and cardnumber_t[], but when an
>>attempt is made to convert a cardnumber_t[] to a varchar[], the connection
>>is dropped.
>
> What's going on here is that array_map thinks it can use fn_extra of the
> passed FmgrInfo for its own purposes.  That means that if the function
> to be called tries to use fn_extra for *its* own purposes, we have a
> conflict that is going to lead to core dumps in most cases.  In other
> words, array_map pretty much doesn't work for calling anything except
> built-in functions.
>
> I think the best solution to this is to require array_map's caller to
> provide the state storage array_map wants, instead of doing it locally.
> Both of the existing callers can easily incorporate array_map's state
> data into their own state structs.  Joe, you have any better ideas?
>

That certainly looks like the least invasive fix for 8.0.x and 7.4.x.

I have thought before that we were overloading fn_extra a bit too much.
Is there any merit in having more than one "extra" member in FmgrInfo
going forward?

Joe
Joe Conway <mail@joeconway.com> writes:
> I have thought before that we were overloading fn_extra a bit too much.
> Is there any merit in having more than one "extra" member in FmgrInfo
> going forward?

Not sure what --- how would you decide which to use, and what stops
there being a conflict on one of them anyway?

The basic rule is that "fn_extra belongs to the called function", and
array_map was definitely breaking that rule.  So I don't think this bug
is a symptom of a system-wide issue.

            regards, tom lane