Thread: coalesce with all nulls can only be assigned to text

coalesce with all nulls can only be assigned to text

From
"Kevin Grittner"
Date:
Here is sample code demonstrating the issue:

test=# create table test_coalesce(f1 int not null, f2 date);
CREATE TABLE
test=# insert into test_coalesce values (1, null);
INSERT 0 1
test=# insert into test_coalesce values (2, coalesce(null, null));
ERROR:  column "f2" is of type date but expression is of type text
HINT:  You will need to rewrite or cast the expression.
test=# create cast (text as date) with function date(text) as
assignment;
ERROR:  cast from type text to type date already exists
test=# create cast (text as date) with function date(text) as
implicit;
ERROR:  cast from type text to type date already exists

The last statement is not something which would make sense to hand
code, but we have a framework which is plugging in the arguments for the
coalesce function at run time.  One solution to this is to modify the
framework to wrap any null with a cast to a type.  Due to the effort and
risk of that approach, I'm looking for alternatives.  Besides, the above
just doesn't make sense to me.

Any suggestions?

-Kevin



Re: coalesce with all nulls can only be assigned to text

From
Tom Lane
Date:
"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
> [ "coalesce(null, null)" yields type TEXT ]

Well, it has to yield *something*.  You'd get the same result from
"coalesce('2006-11-29', '2006-11-30')" ... you might think this looks
like dates, but it's just some untyped literals and the parser chooses
to resolve those as TEXT if there's no other clue anywhere in the
expression.

If you cast at least one of the nulls to DATE, you'll get what you want.

regression=# create table test_coalesce(f1 int not null, f2 date);
CREATE TABLE
regression=# insert into test_coalesce values (2, coalesce(null, null));
ERROR:  column "f2" is of type date but expression is of type text
HINT:  You will need to rewrite or cast the expression.
regression=# insert into test_coalesce values (2, coalesce(null::date, null));
INSERT 0 1

            regards, tom lane

Re: coalesce with all nulls can only be assigned to

From
"Kevin Grittner"
Date:
>>> On Wed, Nov 29, 2006 at 12:15 PM, in message
<21074.1164824140@sss.pgh.pa.us>,
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> "Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
>> [ "coalesce(null, null)" yields type TEXT ]
>
> Well, it has to yield *something*.  You'd get the same result from
> "coalesce('2006- 11- 29', '2006- 11- 30')" ... you might think this
looks
> like dates, but it's just some untyped literals and the parser
chooses
> to resolve those as TEXT if there's no other clue anywhere in the
> expression.

We never do assume that a text literal is a valid date.  I won't bore
you with all the details unless you ask for them, but we're running on
Java and generating literals based on the object type passed to a low
level method.  A null has no type to use as the basis of a cast.

> If you cast at least one of the nulls to DATE, you'll get what you
want.

I realize that, and I'm working on modifying our framework to get type
information down to where we can do that for nulls.  The problem is,
this is a big enough change to potentially cause problems and hold up
the migration to PostgreSQL on the majority of our databases for an
application release cycle (three months), so I'm hoping for a less
drastic workaround.  It seems odd that a bare null works, but a coalesce
of two nulls fails.  It also seems odd that the automatic casting from
text to date fails to cover this.  (I tried creating a cast to cover
this and it told me there already was one.)

Thanks,

-Kevin



Re: coalesce with all nulls can only be assigned to

From
Richard Huxton
Date:
Kevin Grittner wrote:
> Tom Lane <tgl@sss.pgh.pa.us> wrote:
> We never do assume that a text literal is a valid date.  I won't bore
> you with all the details unless you ask for them, but we're running on
> Java and generating literals based on the object type passed to a low
> level method.  A null has no type to use as the basis of a cast.

Unfortunate. Does your method know what type the database column is?

>> If you cast at least one of the nulls to DATE, you'll get what you
> want.
>
> I realize that, and I'm working on modifying our framework to get type
> information down to where we can do that for nulls.  The problem is,
> this is a big enough change to potentially cause problems and hold up
> the migration to PostgreSQL on the majority of our databases for an
> application release cycle (three months), so I'm hoping for a less
> drastic workaround.  It seems odd that a bare null works, but a coalesce
> of two nulls fails.

It's the coalesce that has the problem, not the insert. The coalesce is
deciding that it's working on text, and so returns text.

 > It also seems odd that the automatic casting from
> text to date fails to cover this.  (I tried creating a cast to cover
> this and it told me there already was one.)

There is a cast from text to date, but I don't think it's automatic...
(checks pg_cast) - no, it's marked as explicit. You could try marking
the cast as implicit, but I'd be concerned about unexpected casts occurring.

Another option I can think of: Spot the case where all values in the
coalesce are null and just replace with a single literal null.

--
   Richard Huxton
   Archonet Ltd

Re: coalesce with all nulls can only be assigned to

From
"Kevin Grittner"
Date:
>>> On Wed, Nov 29, 2006 at  1:09 PM, in message
<456DDAFA.3000803@archonet.com>,
Richard Huxton <dev@archonet.com> wrote:
> Kevin Grittner wrote:
>> Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> We never do assume that a text literal is a valid date.  I won't
bore
>> you with all the details unless you ask for them, but we're running
on
>> Java and generating literals based on the object type passed to a
low
>> level method.  A null has no type to use as the basis of a cast.
>
> Unfortunate. Does your method know what type the database column is?

No, it's a method that takes an object and generates a proper SQL
literal for the database product.  (Portability is a big issue.)

>>> If you cast at least one of the nulls to DATE, you'll get what you
>> want.
>>
>> I realize that, and I'm working on modifying our framework to get
type
>> information down to where we can do that for nulls.  The problem
is,
>> this is a big enough change to potentially cause problems and hold
up
>> the migration to PostgreSQL on the majority of our databases for an
>> application release cycle (three months), so I'm hoping for a less
>> drastic workaround.  It seems odd that a bare null works, but a
coalesce
>> of two nulls fails.
>
> It's the coalesce that has the problem, not the insert. The coalesce
is
> deciding that it's working on text, and so returns text.

It seems like maybe it would be worth overloading the coalesce method
to handle this particular case differently.  It might allow some queries
to optimize better.  Maybe.  On the other hand, it doesn't sound like it
comes up often, so it's likely not worth the effort.

>  > It also seems odd that the automatic casting from
>> text to date fails to cover this.  (I tried creating a cast to
cover
>> this and it told me there already was one.)
>
> There is a cast from text to date, but I don't think it's
automatic...
> (checks pg_cast) -  no, it's marked as explicit. You could try
marking
> the cast as implicit, but I'd be concerned about unexpected casts
occurring.

Point taken.  I would only do this as a temporary workaround -- it
doesn't seem like a good permanent solution.  If I were to do this,
would I update the existing row in pg_cast or use the DROP CAST and ADD
CAST statements?

> Another option I can think of: Spot the case where all values in the

> coalesce are null and just replace with a single literal null.

This would have to be done in the JDBC driver's handling of the "{fn
IFNULL" portability escape code.  That might be a decent stop-gap.  I
think I'll do that to support preliminary testing, and work on the
framework changes for the long-term solution.

Thanks,

-Kevin



Re: coalesce with all nulls can only be assigned to

From
Tom Lane
Date:
"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
> Richard Huxton <dev@archonet.com> wrote:
>> It's the coalesce that has the problem, not the insert. The coalesce is
>> deciding that it's working on text, and so returns text.

> It seems like maybe it would be worth overloading the coalesce method
> to handle this particular case differently.

And do what?  The only information you have is that all the inputs are
of unknown type.  You do not get to look at context, because the type
resolution algorithm has to work bottom-up in expressions.

            regards, tom lane

Re: coalesce with all nulls can only be assigned to

From
"Kevin Grittner"
Date:
>>> On Wed, Nov 29, 2006 at  1:38 PM, in message
<21909.1164829090@sss.pgh.pa.us>,
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> "Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
>> Richard Huxton <dev@archonet.com> wrote:
>>> It's the coalesce that has the problem, not the insert. The
coalesce is
>>> deciding that it's working on text, and so returns text.
>
>> It seems like maybe it would be worth overloading the coalesce
method
>> to handle this particular case differently.
>
> And do what?  The only information you have is that all the inputs
are
> of unknown type.

I know this is naive, but, what is the type information of the bare
null?  Could that be used?



Re: coalesce with all nulls can only be assigned to

From
Richard Huxton
Date:
Kevin Grittner wrote:
>>>> On Wed, Nov 29, 2006 at  1:38 PM, in message
> <21909.1164829090@sss.pgh.pa.us>,
> Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> "Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
>>> Richard Huxton <dev@archonet.com> wrote:
>>>> It's the coalesce that has the problem, not the insert. The
> coalesce is
>>>> deciding that it's working on text, and so returns text.
>>
>>> It seems like maybe it would be worth overloading the coalesce
> method
>>> to handle this particular case differently.
>> And do what?  The only information you have is that all the inputs
> are
>> of unknown type.
>
> I know this is naive, but, what is the type information of the bare
> null?  Could that be used?

A literal null is "unknown", other literals are "unknown" or
"unknown-numeric" based on quoting iirc.

Hmm - Tom would it be possible to create a cast to unknown, explicitly
cast this coalesce to unknown and let it implicitly cast back to
whatever the column needs?

--
   Richard Huxton
   Archonet Ltd

Re: coalesce with all nulls can only be assigned to

From
Martijn van Oosterhout
Date:
On Wed, Nov 29, 2006 at 01:45:09PM -0600, Kevin Grittner wrote:
> > And do what?  The only information you have is that all the inputs
> > are of unknown type.
>
> I know this is naive, but, what is the type information of the bare
> null?  Could that be used?

A null can be of any type, string, text, integer, etc. If you have a
bare null in a query, it gets type "unknown" and the system has to
guess. Looking up possible matching operators and functions can help,
but if none of those possibilites help, it gets assigned type "text".
That's why as soon as one entry has a type, it works because the system
can assume the others are of the same type.

Compare this with pointers in C. There you can have a char ponter and
and an integer pointer, both NULL yet they cannot be used
interchangably, they are of different types.

I'm curious how in such a strongly typed language as Java you represent
a null without any associated type. Or does Java not distinguish
either?

Does this help?

Have a nice day,
--
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> From each according to his ability. To each according to his ability to litigate.

Attachment

Re: coalesce with all nulls can only be assigned to

From
"Kevin Grittner"
Date:
>>> On Wed, Nov 29, 2006 at  1:32 PM, in message
<456D8C05.EE98.0025.0@wicourts.gov>, "Kevin Grittner"
<Kevin.Grittner@wicourts.gov> wrote:
>>>> On Wed, Nov 29, 2006 at  1:09 PM, in message
> <456DDAFA.3000803@archonet.com>,
> Richard Huxton <dev@archonet.com> wrote:
>
>> Another option I can think of: Spot the case where all values in
the
>> coalesce are null and just replace with a single literal null.
>
> This would have to be done in the JDBC driver's handling of the "{fn
> IFNULL" portability escape code.  That might be a decent stop- gap.
I
> think I'll do that to support preliminary testing, and work on the
> framework changes for the long- term solution.

The JDBC hack was easy, although nothing to be proud of or suggest for
inclusion in the product.  I'll paste it below for the benefit of anyone
in similar circumstances who finds this thread.

Martijn, Java is indeed strongly typed, but, there is a type hierarchy
with class Object at its root.  The framework passes along collections
where the values are declared as type Object, and the low level routines
count on being able to interrogate the objects to determine the specific
subclass of Object for a value to be able to handle it correctly.  A
null is really the absence of an object, and can not be interrogated for
a specific type.  So far this has not caused us any problems, but I can
see benefits to carrying type information deeper into the framework.  In
particular, there is an opportunity to overload methods and move some of
the type checking to compile time, for a little run-time performance
boost.

Thanks to all for the information and suggestions.

-Kevin


Index: EscapedFunctions.java
===================================================================
RCS file:
/usr/local/cvsroot/pgjdbc/pgjdbc/org/postgresql/jdbc2/EscapedFunctions.java,v
retrieving revision 1.8
diff -c -r1.8 EscapedFunctions.java
*** EscapedFunctions.java    4 Apr 2006 22:52:42 -0000    1.8
--- EscapedFunctions.java    29 Nov 2006 21:18:17 -0000
***************
*** 593,599 ****
              throw new PSQLException(GT.tr("{0} function takes two and
only two arguments.","ifnull"),
                                      PSQLState.SYNTAX_ERROR);
          }
!         return
"coalesce("+parsedArgs.get(0)+","+parsedArgs.get(1)+")";
      }

      /** user translation */
--- 593,604 ----
              throw new PSQLException(GT.tr("{0} function takes two and
only two arguments.","ifnull"),
                                      PSQLState.SYNTAX_ERROR);
          }
!         String arg0 = String.valueOf(parsedArgs.get(0));
!         String arg1 = String.valueOf(parsedArgs.get(1));
!         if ("null".equals(arg0.trim().toLowerCase()) &&
"null".equals(arg1.trim().toLowerCase())){
!             return "null";
!         }
!         return "coalesce("+arg0+","+arg1+")";
      }

      /** user translation */