tags:

views:

473

answers:

1

I have a query on a PostgreSQL system returning a boolean:

my $sth = $dbh->prepare("select 'f'::boolean");
$sth->execute;
my @vals = $sth->fetchrow_array;

According to the DBD::Pg docs,

The current implementation of PostgreSQL returns 't' for true and 'f' for false. From the Perl point of view, this is a rather unfortunate choice. DBD::Pg therefore translates the result for the BOOL data type in a Perlish manner: 'f' becomes the number 0 and 't' becomes the number 1. This way the application does not have to check the database-specific returned values for the data-type BOOL because Perl treats 0 as false and 1 as true. You may set the pg_bool_tf attribute to a true value to change the values back to 't' and 'f' if you wish.

So, that statement should return a 0, which it does, so long as pg_bool_tf returns 0, which it does. However, somewhere along the way JSON::XS (and plain JSON) interprets the returned 0 as a string:

use JSON::XS qw(encode_json);
my $options =
{
    layout => 0,
    show_widget_help => $vals[0] // 1,
};
die encode_json($options);

...dies with:

{"layout":0,"show_widget_help":"0"}

...which would be fine, except that my JavaScript is expecting a boolean there, and the non-empty string "0" gets evaluated to true. Why is the latter 0 quoted and the former not?

According to the JSON::XS docs, this is a main feature:

round-trip integrity

When you serialise a perl data structure using only data types supported by JSON, the deserialised data structure is identical on the Perl level. (e.g. the string "2.0" doesn't suddenly become "2" just because it looks like a number). There minor are exceptions to this, read the MAPPING section below to learn about those.

...which says:

Simple Perl scalars (any scalar that is not a reference) are the most difficult objects to encode: JSON::XS will encode undefined scalars as JSON null values, scalars that have last been used in a string context before encoding as JSON strings, and anything else as number value.

But I never use @vals[0] in a string context. Maybe DBD::Pg uses its boolean 0 as a string somewhere before returning it?

+2  A: 

The JSON::XS doc says the following will be converted to true/false

  • references to the integers 0 and 1, ie. \0 and \1
  • JSON::XS::true and JSON::XS::false

Using one of these should solve your problem

Hasturkun
It's true; however, these values are not what DBD::Pg returns.
Kev
Besides, that'd be the same amount of kludge as "::integer" or "+0". To me, I think either JSON::XS or DBD::Pg are doing something wrong, but I don't know how to figure out which one, or whether that's actually true.
Kev
what does `Data::Dumper` show on your hash? `0`, or `'0'`?
Hasturkun
a peek at `DBD::Pg`'s `dequote_bool` suggests it may be returning the value as the strings "0" and "1", instead of an integerhttp://cpansearch.perl.org/src/TURNSTEP/DBD-Pg-2.13.1/quote.c
Hasturkun
Ah, Dumper shows it as a string, and you're right about quote.c, I was wondering why I couldn't find anything in the actual .pm file. I'll file a report with DBD::Pg. Thanks!!
Kev
@Kev: If you want JSON::XS have true or false, using JSON::XS::true/false isn't a kludge, it's the right thing to do.
ysth
@ysth: Having to re-run through values that are already able to be marked as a specific type is kludge in my app, in my opinion. I'm not saying it's kludge on the part of JSON::XS, if that's what you mean.
Kev
Update: Greg (the DBD::Pg maintainer) is looking into fixing it so it returns integers for integers and booleans, rather than strings for everything.
Kev
Update: the latest DBD::Pg fixes this. Hooray!
Kev