Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support binding native boolean false on 5.36 and newer #129

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .perlcriticrc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
3 changes: 3 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions Pg.pm
Original file line number Diff line number Diff line change
Expand Up @@ -4455,6 +4455,13 @@ set the L<pg_bool_tf|/pg_bool_tf (boolean)> 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<builtin/is_bool>) can be used as placeholder values. On older
versions of Perl, false values returned by built-in operators (such
as C<!!0>) must be converted to one of the above false values, or
bound with C<pg_type => 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,
Expand Down
17 changes: 14 additions & 3 deletions dbdimp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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';
Expand Down
23 changes: 18 additions & 5 deletions t/12placeholders.t
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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',
Expand All @@ -827,6 +828,7 @@ undef => 'NULL',
'false' => 'FALSE',
'FALSE' => 'FALSE',
'' => 'FALSE',
'real false' => 'FALSE',
12 => 'ERROR',
'01' => 'ERROR',
'00' => 'ERROR',
Expand All @@ -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);
Expand Down Expand Up @@ -887,6 +891,15 @@ $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;

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 \?};
Expand Down
Loading