From 4504771da9e6176cc6b37258de103bbb73232ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Fri, 27 Sep 2024 17:23:38 +0100 Subject: [PATCH 1/2] Reset pg_bool_tf after testing it --- t/12placeholders.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/12placeholders.t b/t/12placeholders.t index 4f5a3e50..c241563f 100644 --- a/t/12placeholders.t +++ b/t/12placeholders.t @@ -887,6 +887,7 @@ $sth->bind_param(2,'TRUE',SQL_BOOLEAN); $sth->execute(104,'','Boolean empty string attempt number four'); $dbh->{pg_bool_tf} = 1; is_deeply ($sth->fetch, [104,'f'], $t); +$dbh->{pg_bool_tf} = 0; ## Test of placeholder escaping. Enabled by default, so let's jump right in $t = q{Basic placeholder escaping works via backslash-question mark for \?}; From eb543736946fad415c69c82a9441156d090b7133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Fri, 27 Sep 2024 19:19:09 +0100 Subject: [PATCH 2/2] Support binding native boolean false on 5.36 and newer Fixes #125 --- .perlcriticrc | 2 +- Changes | 3 +++ Pg.pm | 7 +++++++ dbdimp.c | 17 ++++++++++++++--- t/12placeholders.t | 22 +++++++++++++++++----- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/.perlcriticrc b/.perlcriticrc index f647a7ab..3a2431ab 100644 --- a/.perlcriticrc +++ b/.perlcriticrc @@ -3,7 +3,7 @@ profile-strictness = quiet exclude = Mardem [Documentation::PodSpelling] -stop_words = ActiveKids afterwards arrayref arrayrefs attr autocommit AutoCommit AutoInactiveDestroy backend bitmask bool boolean Bunce bytea CachedKids cancelled ChildHandles ChopBlanks CompatMode CursorName datatype Datatype datatypes dbd DBD dbdpg dbh DBI deallocation deallocated dev dr DSN enum ErrCount errstr fd FetchHashKeyName filename func getfd getline github HandleError HandleSetErr hashref hashrefs InactiveDestroy JSON largeobject len libpq LongReadLen LongTruncOk lseg Mergl Momjian Mullane nullable NULLABLE Oid OID onwards param ParamTypes ParamValues perl Perlish PgBouncer pgbuiltin pgend pglibpq pglogin pgprefix pgquote PGSERVICE PGSERVICEFILE pgsql pgstart PGSYSCONFDIR PID Postgres PostgreSQL PQexecParams PQexecPrepared PrintError PrintWarn pseudotype RaiseError README ReadOnly RowCache RowCacheSize RowsInCache runtime Sabino savepoint savepoints Savepoints schemas ShowErrorStatement SQL SQLSTATE SSL sslmode STDERR STDIN STDOUT subdirectory tablename tablespace tablespaces TaintIn TaintOut TraceLevel tuple typename undef username Username UTF varchar +stop_words = ActiveKids afterwards arrayref arrayrefs attr autocommit AutoCommit AutoInactiveDestroy backend bitmask bool boolean Bunce bytea CachedKids cancelled ChildHandles ChopBlanks CompatMode CursorName datatype Datatype datatypes dbd DBD dbdpg dbh DBI deallocation deallocated dev dr DSN enum ErrCount errstr fd FetchHashKeyName filename func getfd getline github HandleError HandleSetErr hashref hashrefs InactiveDestroy JSON largeobject len libpq LongReadLen LongTruncOk lseg Mergl Momjian Mullane nullable NULLABLE Oid OID onwards param ParamTypes ParamValues perl Perlish PgBouncer pgbuiltin pgend pglibpq pglogin pgprefix pgquote PGSERVICE PGSERVICEFILE pgsql pgstart PGSYSCONFDIR PID Postgres PostgreSQL PQexecParams PQexecPrepared PrintError PrintWarn pseudotype RaiseError README ReadOnly RowCache RowCacheSize RowsInCache runtime Sabino savepoint savepoints Savepoints schemas ShowErrorStatement SQL SQLSTATE SSL sslmode STDERR STDIN STDOUT stringify subdirectory tablename tablespace tablespaces TaintIn TaintOut TraceLevel tuple typename undef username Username UTF varchar [-Bangs::ProhibitBitwiseOperators] [-Bangs::ProhibitCommentedOutCode] diff --git a/Changes b/Changes index 484153f5..dee5aeea 100644 --- a/Changes +++ b/Changes @@ -2,6 +2,9 @@ Changes for the DBD::Pg module RT refers to rt.cpan.org + - Support binding native boolean false on Perl 5.36 and newer + [Dagfinn Ilmari Mannsåker] + Version 3.18.0 (released December 6, 2023) - Support new PQclosePrepared function, added in Postgres 17 diff --git a/Pg.pm b/Pg.pm index b4665538..1654aa03 100644 --- a/Pg.pm +++ b/Pg.pm @@ -4455,6 +4455,13 @@ set the L attribute to a true value to change Boolean values can be passed to PostgreSQL as TRUE, 't', 'true', 'y', 'yes' or '1' for true and FALSE, 'f', 'false', 'n', 'no' or '0' for false. +On Perl 5.36 and newer, distinguished boolean values (see +L) can be used as placeholder values. On older +versions of Perl, false values returned by built-in operators (such +as C) must be converted to one of the above false values, or +bound with C PG_BOOL>, since they stringify to the empty +string. + =head2 Schema support The PostgreSQL schema concept may differ from those of other databases. In a nutshell, diff --git a/dbdimp.c b/dbdimp.c index ab74e09b..2bfab354 100644 --- a/dbdimp.c +++ b/dbdimp.c @@ -17,6 +17,10 @@ #define atoll(X) _atoi64(X) #endif +#ifndef SvIsBOOL +#define SvIsBOOL(sv) DBDPG_FALSE +#endif + #define DEBUG_LAST_RESULT 0 #if PGLIBVERSION < 80300 @@ -2625,9 +2629,16 @@ int dbd_bind_ph (SV * sth, imp_sth_t * imp_sth, SV * ph_name, SV * newvalue, IV (void)SvUPGRADE(newvalue, SVt_PV); if (SvOK(newvalue)) { - /* get the right encoding, without modifying the caller's copy */ - newvalue = pg_rightgraded_sv(aTHX_ newvalue, imp_dbh->pg_utf8_flag && PG_BYTEA!=currph->bind_type->type_id); - value_string = SvPV(newvalue, currph->valuelen); + if (SvIsBOOL(newvalue)) { + /* bind native booleans as 1/0 */ + value_string = SvTRUE(newvalue) ? "1" : "0"; + currph->valuelen = 1; + } + else { + /* get the right encoding, without modifying the caller's copy */ + newvalue = pg_rightgraded_sv(aTHX_ newvalue, imp_dbh->pg_utf8_flag && PG_BYTEA!=currph->bind_type->type_id); + value_string = SvPV(newvalue, currph->valuelen); + } Renew(currph->value, currph->valuelen+1, char); /* freed in dbd_st_destroy */ Copy(value_string, currph->value, currph->valuelen+1, char); currph->value[currph->valuelen] = '\0'; diff --git a/t/12placeholders.t b/t/12placeholders.t index c241563f..8b0c3dba 100644 --- a/t/12placeholders.t +++ b/t/12placeholders.t @@ -17,7 +17,7 @@ my $dbh = connect_database(); if (! $dbh) { plan skip_all => 'Connection to database failed, cannot continue testing'; } -plan tests => 261; +plan tests => 264; my $t='Connect to database for placeholder testing'; isnt ($dbh, undef, $t); @@ -819,6 +819,7 @@ undef => 'NULL', '0e0' => 'TRUE', '0 but true' => 'TRUE', '0 BUT TRUE' => 'TRUE', +'real true' => 'TRUE', 'f' => 'FALSE', 'F' => 'FALSE', 0 => 'FALSE', @@ -827,6 +828,7 @@ undef => 'NULL', 'false' => 'FALSE', 'FALSE' => 'FALSE', '' => 'FALSE', +'real false' => 'FALSE', 12 => 'ERROR', '01' => 'ERROR', '00' => 'ERROR', @@ -839,10 +841,12 @@ undef => 'NULL', ); while (my ($name,$res) = each %booltest) { - $name = undef if $name eq 'undef'; - $t = sprintf 'Boolean quoting of %s', - defined $name ? qq{"$name"} : 'undef'; - eval { $result = $dbh->quote($name, {pg_type => PG_BOOL}); }; + my ($bool, $desc) = + $name eq 'undef' ? (undef, $name) : + $name =~ /\Areal/ ? (!!($name =~ / true\z/), $name) : + ($name, qq{"$name"}); + $t = "Boolean quoting of $desc", + eval { $result = $dbh->quote($bool, {pg_type => PG_BOOL}); }; if ($@) { if ($res eq 'ERROR' and $@ =~ /Invalid boolean/) { pass ($t); @@ -889,6 +893,14 @@ $dbh->{pg_bool_tf} = 1; is_deeply ($sth->fetch, [104,'f'], $t); $dbh->{pg_bool_tf} = 0; +SKIP: { + skip 'Cannot test native false without builtin::is_bool', 1 unless defined &builtin::is_bool; + $t = q{Inserting into a boolean column with native false works}; + $sth = $dbh->prepare($SQL); + $sth->execute(105, !!0, 'Boolean native false'); + is_deeply ($sth->fetch, [105, 0], $t); +} + ## Test of placeholder escaping. Enabled by default, so let's jump right in $t = q{Basic placeholder escaping works via backslash-question mark for \?};