Version in base suite: 1.647-1+deb13u1 Base version: libdbi-perl_1.647-1+deb13u1 Target version: libdbi-perl_1.652-2~deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libd/libdbi-perl/libdbi-perl_1.647-1+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libd/libdbi-perl/libdbi-perl_1.652-2~deb13u1.dsc CONTRIBUTING.md | 44 + ChangeLog | 44 + DBI.pm | 157 ++--- DBI.xs | 293 ++++------ DBIXS.h | 16 Driver.xst | 6 Driver_xst.h | 2 LICENSE | 2 MANIFEST | 73 +- META.json | 44 - META.yml | 10 Makefile.PL | 47 + SECURITY.md | 16 cpanfile | 16 dbipport.h | 77 ++ dbixs_rev.h | 6 dbixs_rev.pl | 16 debian/changelog | 77 ++ debian/copyright | 6 debian/libdbi-perl.docs | 2 debian/patches/0001-Fix-test-for-32bit-perl-report-in-PR-189.patch | 37 + debian/patches/series | 3 debian/patches/t__40profile.t__NTP.patch | 5 ex/unicode_test.pl | 1 lib/DBD/DBM.pm | 39 + lib/DBD/File.pm | 41 + lib/DBD/File/Developers.pod | 39 - lib/DBD/File/HowTo.pod | 36 - lib/DBD/File/Roadmap.pod | 2 lib/DBD/Gofer/Transport/Base.pm | 2 lib/DBD/Gofer/Transport/stream.pm | 2 lib/DBD/Mem.pm | 6 lib/DBD/Proxy.pm | 8 lib/DBD/Sponge.pm | 56 + lib/DBI/Changes.pm | 156 +++++ lib/DBI/DBD.pm | 4 lib/DBI/DBD/Metadata.pm | 6 lib/DBI/DBD/SqlEngine.pm | 79 +- lib/DBI/DBD/SqlEngine/Developers.pod | 28 lib/DBI/DBD/SqlEngine/HowTo.pod | 36 - lib/DBI/Gofer/Transport/Base.pm | 2 lib/DBI/Profile.pm | 7 lib/DBI/ProfileData.pm | 11 lib/DBI/ProxyServer.pm | 4 lib/DBI/PurePerl.pm | 4 lib/DBI/SQL/Nano.pm | 9 lib/DBI/W32ODBC.pm | 10 lib/Win32/DBIODBC.pm | 58 - t/05concathash.t | 2 t/06attrs.t | 2 t/07kids.t | 2 t/10examp.t | 10 t/11fetch.t | 3 t/13taint.t | 8 t/14utf8.t | 6 t/31methcache.t | 36 - t/35thrclone.t | 18 t/40profile.t | 51 + t/41prof_dump.t | 4 t/42prof_data.t | 4 t/49dbd_file.t | 12 t/50dbm_simple.t | 74 ++ t/51dbm_file.t | 284 +++++---- t/60preparse.t | 18 t/65transact.t | 4 t/72childhandles.t | 4 t/82sponge.t | 72 ++ t/90sql_type_cast.t | 22 68 files changed, 1481 insertions(+), 800 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpbd0lefg6/libdbi-perl_1.647-1+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpbd0lefg6/libdbi-perl_1.652-2~deb13u1.dsc: no acceptable signature found diff -Nru libdbi-perl-1.647/CONTRIBUTING.md libdbi-perl-1.652/CONTRIBUTING.md --- libdbi-perl-1.647/CONTRIBUTING.md 1970-01-01 00:00:00.000000000 +0000 +++ libdbi-perl-1.652/CONTRIBUTING.md 2026-07-04 14:24:41.000000000 +0000 @@ -0,0 +1,44 @@ +# How to Contribute + +Help with DBI is always welcome in all areas: code, tests, documentation. +Just reach out to se where we think the pressure point are. + +## Issues + +Bugs and problems can be reported at: + +https://github.com/perl5-dbi/dbi/issues + +## Questions + +Questions about usage can be answered in a number of places: + +### IRC + +You may find helpful people at #dbi on irc.perl.org - people might not be +around the moment you ask the question, but an answer is likely to be given. + +## Testing + +You can help by testing DBI as widely as possible. Running the tests +on a wide variety of platforms in as many configurations as possible (32bit +vs 64-bit, threaded vs non-threaded, big-endian vs lower endian, quadmath, +parallel testing vs single process testing, limited resources vs extreme +resources), database types and versions, DBI versions, and other factors is +a great help in uncovering problems and improving DBI. + +## Code development + +Development happens in a git repo. The canonical location is currently +at https://github.com/perl5-dbi/dbi/ + +Create your own copy of the repo by running: + +git clone https://github.com/perl5-dbi/dbi.git + +## Guides + +### Code style and layout + +This code has long history and was developed by multiple authors. Try to +keep the style of the new or modified code in line with the surrounding code. diff -Nru libdbi-perl-1.647/ChangeLog libdbi-perl-1.652/ChangeLog --- libdbi-perl-1.647/ChangeLog 2025-01-20 07:59:52.000000000 +0000 +++ libdbi-perl-1.652/ChangeLog 2026-08-15 08:50:43.000000000 +0000 @@ -1,3 +1,44 @@ +1.652 - 2026-08-15, H.Merijn Brand & Robert Rothenberg + * DBI now requires perl-5.12, driven by CVE's + * Fix test for perl configured without threads (caught by perl-5.45.1) + * Small doc fix, (issue#155), thanks Dick Franks + * Add suggested dependency (issue#145), thanks Dick Franks + * Force placeholder limit on :# and :p# too (CVE-2026-73194) + * Limit statements to 292 Mb in preparse (CVE-2026-73193) + * Add a security policy (issue#174) + * Add dbm_updatable_key attribute to DBD::DBM to configure how keys are updated + * Fix missing import in DBI::DBD::SqlEngine + * Fix !Caller2 caller loss when $^P is set (PR#184) (Thanks Paul) + +1.651 - 2026-07-14, H.Merijn Brand & Robert Rothenberg + * Fix inverted comparisons for strings in DBI::SQL::Nano (CVE-2026-15043) + * Document that IS NULL matches empty strings in DBI::SQL::Nano + * Fix DBD::File to ensure that the table is not a symlink outside of f_dir (CVE-2026-15392) + * Fix an out-of-bounds error when a statement handle has no fields but the source row is not empty (CVE-2026-60082) + * Add an overridable upper bound $MAX_PATH_DEPTH for DBI::ProfileData (CVE-2026-60081) + * *** WARNING: Next release will require perl-5.12 *** + +1.650 - 2026-07-06, H.Merijn Brand & Robert Rothenberg + * Set a hard limit of 99999 on '?' placeholders (CVE-2026-14739) + * Fix out-of-bounds read in preparse of SQL that starts with a comment (CVE-2026-14740) + * Fix code injection via Profile DSN attribute or DBI_PROFILE variable (CVE-2026-14380) + * Update dbipport.h to Devel::PPPort-3.73 + * Require Test::More 0.96 (tests will otherwise fail on pristine perl-5.12) + +1.649 - 2026-06-22, H.Merijn Brand + * Extra Cwd::abs_path required for Windows + +1.648 - 2026-06-04, H.Merijn Brand + * Correct sprintf usage for trace_msg (issue#132) + * Add DBIXS_VERSION & DBIXS_RELEASE to dbixs_rev.h + * Remove -Wbad-function-cast + * Fix possible stack overflow (old issue already noted by Tim) (CVE-2026-9698) + * Do not allow table source locations outside explicit given folders + * DBD::Sponge PRECISION handling (pr#12, pilcrow) + * Fix possible buffer overflow in preparse (CVE-2026-10879) + * Skip mismatching .so's (Greg, PR#81) + * Fix cast warning in dbi_get_state (Greg, PR#182) + 1.647 - 2025-01-20, H.Merijn Brand * Spellcheck * Fix Makefile rules for Changes (Windows case issue) @@ -6,12 +47,11 @@ 1.646 - 2025-01-11, H.Merijn Brand * Remove "experimental" tag from statistics_info () (issue#134) - * RT tickets moved to github issues (rwfranks++) + * RT tickets moved to github issues (Dick Franks++) - All RT tickets now marked as resolved with reference to GitHub issue * Fix install issue (issue #168) 1.645 - 2024-09-03, H.Merijn Brand - * Move developer tests to xt/ * Make Changes match CPAN::Changes::Spec and regen DBI::Changes from that * Fixes for modern gcc i.c.w. recent perl (Daniël) diff -Nru libdbi-perl-1.647/DBI.pm libdbi-perl-1.652/DBI.pm --- libdbi-perl-1.647/DBI.pm 2025-01-17 13:24:27.000000000 +0000 +++ libdbi-perl-1.652/DBI.pm 2026-07-24 15:14:05.000000000 +0000 @@ -1,7 +1,7 @@ # $Id$ # vim: ts=8:sw=4:et # -# Copyright (c) 2024-2025 DBI Team +# Copyright (c) 2024-2026 DBI Team # Copyright (c) 1994-2024 Tim Bunce Ireland # # See COPYRIGHT section in pod text below for usage and distribution rights. @@ -9,14 +9,14 @@ package DBI; -require 5.008001; +require 5.012000; use strict; use warnings; our ($XS_VERSION, $VERSION); BEGIN { -$VERSION = "1.647"; # ==> ALSO update the version in the pod text below! +$VERSION = "1.652"; # ==> ALSO update the version in the pod text below! $XS_VERSION = $VERSION; $VERSION =~ tr/_//d; } @@ -148,7 +148,7 @@ =head2 NOTES -This is the DBI specification that corresponds to DBI version 1.647 +This is the DBI specification that corresponds to DBI version 1.652 (see L for details). The DBI is evolving at a steady pace, so it's good to check that @@ -405,8 +405,8 @@ common => { # Interface methods common to all DBI handle classes 'DESTROY' => { O=>0x004|0x10000 }, - 'CLEAR' => $keeperr, - 'EXISTS' => $keeperr, + 'CLEAR' => $keeperr, + 'EXISTS' => $keeperr, 'FETCH' => { O=>0x0404 }, 'FETCH_many' => { O=>0x0404 }, 'FIRSTKEY' => $keeperr, @@ -414,12 +414,12 @@ 'STORE' => { O=>0x0418 | 0x4 }, 'DELETE' => { O=>0x0404 }, can => { O=>0x0100 }, # special case, see dispatch - debug => { U =>[1,2,'[$debug_level]'], O=>0x0004 }, # old name for trace - dump_handle => { U =>[1,3,'[$message [, $level]]'], O=>0x0004 }, + debug => { U =>[1,2,'[$debug_level]'], O=>0x0004 }, # old name for trace + dump_handle => { U =>[1,3,'[$message [, $level]]'], O=>0x0004 }, err => $keeperr, errstr => $keeperr, state => $keeperr, - func => { O=>0x0006 }, + func => { O=>0x0006 }, parse_trace_flag => { U =>[2,2,'$name'], O=>0x0404, T=>8 }, parse_trace_flags => { U =>[2,2,'$flags'], O=>0x0404, T=>8 }, private_data => { U =>[1,1], O=>0x0004 }, @@ -441,15 +441,15 @@ db => { # Database Session Class Interface data_sources => { U =>[1,2,'[\%attr]' ], O=>0x0200 }, take_imp_data => { U =>[1,1], O=>0x10000 }, - clone => { U =>[1,2,'[\%attr]'], T=>0x200 }, - connected => { U =>[1,0], O => 0x0004, T=>0x200, H=>3 }, - begin_work => { U =>[1,2,'[ \%attr ]'], O=>0x0400, T=>0x1000 }, - commit => { U =>[1,1], O=>0x0480|0x0800, T=>0x1000 }, - rollback => { U =>[1,1], O=>0x0480|0x0800, T=>0x1000 }, - 'do' => { U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x3200 }, + clone => { U =>[1,2,'[\%attr]'], T=>0x200 }, + connected => { U =>[1,0], O => 0x0004, T=>0x200, H=>3 }, + begin_work => { U =>[1,2,'[ \%attr ]'], O=>0x0400, T=>0x1000 }, + commit => { U =>[1,1], O=>0x0480|0x0800, T=>0x1000 }, + rollback => { U =>[1,1], O=>0x0480|0x0800, T=>0x1000 }, + 'do' => { U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x3200 }, last_insert_id => { U =>[1,6,'[$catalog [,$schema [,$table_name [,$field_name [, \%attr ]]]]]'], O=>0x2800 }, - preparse => { }, # XXX - prepare => { U =>[2,3,'$statement [, \%attr]'], O=>0xA200 }, + preparse => { }, # XXX + prepare => { U =>[2,3,'$statement [, \%attr]'], O=>0xA200 }, prepare_cached => { U =>[2,4,'$statement [, \%attr [, $if_active ] ]'], O=>0xA200 }, selectrow_array => { U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, selectrow_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, @@ -458,11 +458,11 @@ selectall_array =>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, selectall_hashref=>{ U =>[3,0,'$statement, $keyfield [, \%attr [, @bind_params ] ]'], O=>0x2000 }, selectcol_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, - ping => { U =>[1,1], O=>0x0404 }, - disconnect => { U =>[1,1], O=>0x0400|0x0800|0x10000, T=>0x200 }, - quote => { U =>[2,3, '$string [, $data_type ]' ], O=>0x0430, T=>2 }, + ping => { U =>[1,1], O=>0x0404 }, + disconnect => { U =>[1,1], O=>0x0400|0x0800|0x10000, T=>0x200 }, + quote => { U =>[2,3, '$string [, $data_type ]' ], O=>0x0430, T=>2 }, quote_identifier=> { U =>[2,6, '$name [, ...] [, \%attr ]' ], O=>0x0430, T=>2 }, - rows => $keeperr, + rows => $keeperr, tables => { U =>[1,6,'$catalog, $schema, $table, $type [, \%attr ]' ], O=>0x2200 }, table_info => { U =>[1,6,'$catalog, $schema, $table, $type [, \%attr ]' ], O=>0x2200|0x8800 }, @@ -488,11 +488,11 @@ execute_array => { U =>[2,0,'\\%attribs [, @args]'], O=>0x1040|0x4000 }, execute_for_fetch => { U =>[2,3,'$fetch_sub [, $tuple_status]'], O=>0x1040|0x4000 }, - fetch => undef, # alias for fetchrow_arrayref + fetch => undef, # alias for fetchrow_arrayref fetchrow_arrayref => undef, fetchrow_hashref => undef, fetchrow_array => undef, - fetchrow => undef, # old alias for fetchrow_array + fetchrow => undef, # old alias for fetchrow_array fetchall_arrayref => { U =>[1,3, '[ $slice [, $max_rows]]'] }, fetchall_hashref => { U =>[2,2,'$key_field'] }, @@ -501,8 +501,8 @@ blob_copy_to_file => { U =>[3,3,'$field, $filename_or_handleref'] }, dump_results => { U =>[1,5,'$maxfieldlen, $linesep, $fieldsep, $filehandle'] }, more_results => { U =>[1,1] }, - finish => { U =>[1,1] }, - cancel => { U =>[1,1], O=>0x0800 }, + finish => { U =>[1,1] }, + cancel => { U =>[1,1], O=>0x0800 }, rows => $keeperr, _get_fbav => undef, @@ -681,15 +681,15 @@ my $dbh; unless ($dbh = $drh->$connect_meth($dsn, $user, $pass, $attr)) { - $user = '' if !defined $user; - $dsn = '' if !defined $dsn; + $user //= ''; + $dsn //= ''; # $drh->errstr isn't safe here because $dbh->DESTROY may not have # been called yet and so the dbh errstr would not have been copied # up to the drh errstr. Certainly true for connect_cached! my $errstr = $DBI::errstr; # Getting '(no error string)' here is a symptom of a ref loop - $errstr = '(no error string)' if !defined $errstr; - my $msg = "$class connect('$dsn','$user',...) failed: $errstr"; + $errstr //= '(no error string)'; + my $msg = "$class connect('$dsn','$user',...) failed: $errstr"; DBI->trace_msg(" $msg\n"); # XXX HandleWarn unless ($attr->{HandleError} && $attr->{HandleError}->($msg, $drh, $dbh)) { @@ -1082,8 +1082,8 @@ sub neat_list { my ($listref, $maxlen, $sep) = @_; - $maxlen = 0 unless defined $maxlen; # 0 == use internal default - $sep = ", " unless defined $sep; + $maxlen //= 0; # 0 == use internal default + $sep //= ", "; join($sep, map { neat($_,$maxlen) } @$listref); } @@ -1117,7 +1117,7 @@ return "" if !$diff and $a_desc eq $b_desc; $diff ||= "Strings contain the same sequence of characters" - if length($a); + if length($a); $diff .= "\n" if $diff; return "a: $a_desc\nb: $b_desc\n$diff"; } @@ -1249,7 +1249,7 @@ 'State' => \$h_state_store, # Holder for DBI::state 'Err' => \$h_err_store, # Holder for DBI::err 'Errstr' => \$h_errstr_store, # Holder for DBI::errstr - 'TraceLevel' => 0, + 'TraceLevel' => 0, FetchHashKeyName=> 'NAME', %$initial_attr, }; @@ -1441,7 +1441,7 @@ sub visit_child_handles { my ($h, $code, $info) = @_; - $info = {} if not defined $info; + $info //= {}; for my $ch (@{ $h->{ChildHandles} || []}) { next unless $ch; my $child_info = $code->($ch, $info) @@ -1460,8 +1460,8 @@ sub default_user { my ($drh, $user, $pass, $attr) = @_; - $user = $ENV{DBI_USER} unless defined $user; - $pass = $ENV{DBI_PASS} unless defined $pass; + $user //= $ENV{DBI_USER}; + $pass //= $ENV{DBI_PASS}; return ($user, $pass); } @@ -1487,7 +1487,7 @@ join "!\001", $dsn, $user, $auth, DBI::_concat_hash_sorted($attr, "=\001", ",\001", 0, 0) }; my $dbh = $cache->{$key}; - $drh->trace_msg(sprintf(" connect_cached: key '$key', cached dbh $dbh\n", DBI::neat($key), DBI::neat($dbh))) + $drh->trace_msg(sprintf(" connect_cached: key '%s', cached dbh %s\n", DBI::neat($key), DBI::neat($dbh))) if (($DBI::dbi_debug & 0xF) >= 4); my $cb = $attr->{Callbacks}; # take care not to autovivify @@ -1981,7 +1981,7 @@ $maxlen = @$ary if !$maxlen || @$ary > $maxlen; } # if there are no arrays then execute scalars once - $maxlen = 1 unless defined $maxlen; + $maxlen //= 1; my @bind_ids = 1..keys(%hash_of_arrays); my $tuple_idx = 0; @@ -2046,7 +2046,7 @@ # we copy the array here because fetch (currently) always # returns the same array ref. XXX if ($slice && @$slice) { - $max_rows = -1 unless defined $max_rows; + $max_rows //= -1; push @rows, [ @{$row}[ @$slice] ] while($max_rows-- and $row = $sth->fetch); } @@ -3870,15 +3870,15 @@ Type: code ref, inherited -The C attribute can be used to provide your own alternative behaviour -in case of errors. If set to a reference to a subroutine then that +The C attribute can be used to provide your own alternative +behaviour in case of errors. If set to a reference to a subroutine then that subroutine is called when an error is detected (at the same point that C and C are handled). It is called also when C is enabled and a warning is detected. -The subroutine is called with three parameters: the error message -string that C, C or C would use, -the DBI handle being used, and the first value being returned by +The subroutine is called with three parameters: the error message string that +C, C or C would use, the DBI handle being +used (dbh, sth or drh as appropriate) and the first value being returned by the method that failed (typically undef). If the subroutine returns a false value then the C, C @@ -4358,7 +4358,7 @@ A more common application for callbacks is setting connection state only when a new connection is made (by connect() or connect_cached()). Adding a callback to the connected method (when using C) or via -C (when useing connect_cached()>) makes this easy. +C (when using connect_cached()>) makes this easy. The connected() method is a no-op by default (unless you subclass the DBI and change it). The DBI calls it to indicate that a new connection has been made and the connection attributes have all been set. You can give it a bit of @@ -6339,7 +6339,7 @@ else { for my $tuple (0..@last_names-1) { my $status = $tuple_status[$tuple]; - $status = [0, "Skipped"] unless defined $status; + $status //= [0, "Skipped"]; next unless ref $status; printf "Failed to insert (%s, %s): %s\n", $first_names[$tuple], $last_names[$tuple], $status->[1]; @@ -6899,9 +6899,9 @@ print "$row{region}: $row{sales}\n"; } -but has a small drawback: If data already fetched call to L -will flush current values. If you want to bind_columns after you have fetched -you can use: +but has a small drawback: If the data has already been fetched, the call to +L will flush current values. If you want to bind_columns after +you have fetched, you can use: use feature "refaliasing"; no warnings "experimental::refaliasing"; @@ -6914,7 +6914,7 @@ use Data::Alias; alias @$data{ $sth->{NAME_lc}->@* } = @$row; -This is useful in situations when you have many left joins, but wanna to join +This is useful in situations when you have many left joins, but want to join your %$data hash to only subset of fetched values. =head3 C @@ -7137,7 +7137,8 @@ It is possible that the keys in the hash returned by C are not exactly the same as those implied by the prepared statement. For example, DBD::Oracle translates 'C' placeholders into 'C<:pN>' -where N is a sequence number starting at 1. +where N is a sequence number starting at C<1> with a hard limit of +C<99999>. * Values: @@ -7239,7 +7240,8 @@ It is also possible that the keys in the hash returned by C are not exactly the same as those implied by the prepared statement. For example, DBD::Oracle translates 'C' -placeholders into 'C<:pN>' where N is a sequence number starting at 1. +placeholders into 'C<:pN>' where N is a sequence number starting at +C<1> with a hard limit of C<99999>. =head3 C @@ -7443,7 +7445,7 @@ while($row = $sth->fetchrow_arrayref) { # this is a fast and simple way to deal with nulls: - foreach (@$row) { $_ = '' unless defined } + $_ //= '' for @$row; print "@$row\n"; } @@ -7485,28 +7487,13 @@ Using DBI with perl threads is not yet recommended for production environments. For more information see -L - -Note: There is a bug in perl 5.8.2 when configured with threads and -debugging enabled (bug #24463) which would cause some DBI tests to fail. -These tests have been disabled for perl-5.8.2 and below. - -Tests for inner method cache are disabled for perl-5.10.x +L =head2 Signal Handling and Canceling Operations [The following only applies to systems with unix-like signal handling. I'd welcome additions for other systems, especially Windows.] -The first thing to say is that signal handling in Perl versions less -than 5.8 is I safe. There is always a small risk of Perl -crashing and/or core dumping when, or after, handling a signal -because the signal could arrive and be handled while internal data -structures are being changed. If the signal handling code -used those same internal data structures it could cause all manner -of subtle and not-so-subtle problems. The risk was reduced with -5.4.4 but was still present in all perls up through 5.8.0. - Beginning in perl 5.8.0 perl implements 'safe' signal handling if your system has the POSIX sigaction() routine. Now when a signal is delivered perl just makes a note of it but does I run the @@ -7628,11 +7615,10 @@ Similar techniques can be used for canceling statement execution. -Unfortunately, this solution is somewhat messy, and it does I work with -perl versions less than perl 5.8 where C appears to be broken. +Unfortunately, this solution is somewhat messy. -For a cleaner implementation that works across perl versions, see Lincoln Baxter's -Sys::SigAction module at L. +For a cleaner implementation that works across perl versions, see +Lincoln Baxter's Sys::SigAction module at L. The documentation for Sys::SigAction includes an longer discussion of this problem, and a DBD::Oracle test script. @@ -7776,10 +7762,10 @@ When tracking down memory leaks using tools like L you'll find that some DBI internals are reported as 'leaking' memory. This is very unlikely to be a real leak. The DBI has various caches to improve -performance and the apparrent leaks are simply the normal operation of these +performance and the apparent leaks are simply the normal operation of these caches. -The most frequent sources of the apparrent leaks are L, +The most frequent sources of the apparent leaks are L, L and L. For example http://stackoverflow.com/questions/13338308/perl-dbi-memory-leak @@ -7912,13 +7898,10 @@ tie operations are not available to the PerlIO methods used by the DBI. -=item * -PerlIO layer support requires Perl version 5.8 or higher. - =back -As of version 5.8, Perl provides the ability to layer various -"disciplines" on an open filehandle via the L module. +Perl provides the ability to layer various "disciplines" on an open +filehandle via the L module. A simple example of using PerlIO layers is to use a scalar as the output: @@ -8317,7 +8300,7 @@ The DBI developer group (2024-2024) All rights reserved. You may distribute under the terms of either the GNU General Public -License or the Artistic License, as specified in the Perl 5.10.0 README file. +License or the Artistic License, as specified in the Perl 5.12.0 README file. =head1 SUPPORT / WARRANTY @@ -8342,8 +8325,10 @@ =head1 ACKNOWLEDGEMENTS +The creator: Tim Bunce! + I would like to acknowledge the valuable contributions of the many -people I have worked with on the DBI project, especially in the early +people Tim has worked with on the DBI project, especially in the early years (1992-1994). In no particular order: Kevin Stock, Buzz Moschetti, Kurt Andersen, Ted Lemon, William Hails, Garth Kennedy, Michael Peppler, Neil S. Briscoe, Jeff Urlwin, David J. Hughes, Jeff Stander, @@ -8367,16 +8352,20 @@ A couple of specific DBI features have been sponsored by enlightened companies: -The development of the swap_inner_handle() method was sponsored by BizRate.com (L) +The development of the swap_inner_handle() method was sponsored by BizRate.com The development of DBD::Gofer and related modules was sponsored by Shopzilla.com (L). +After the release of version 1.643, the development was lead by H.Merijn Brand +with the help of the DBI team. Thanks to all that added issues, did Pull +Requests or helped in any way to keep the DBI healthy. + =head1 CONTRIBUTING As you can see above, many people have contributed to the DBI and drivers in many ways over many years. -If you'd like to help then see L. +If you'd like to help then see F. If you'd like the DBI to do something new or different then a good way to make that happen is to do it yourself and send me a patch to the diff -Nru libdbi-perl-1.647/DBI.xs libdbi-perl-1.652/DBI.xs --- libdbi-perl-1.647/DBI.xs 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/DBI.xs 2026-08-15 08:29:58.000000000 +0000 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (c) 2024-2025 DBI Team + * Copyright (c) 2024-2026 DBI Team * Copyright (c) 1994-2024 Tim Bunce Ireland. * * See COPYRIGHT section in DBI.pm for usage and distribution rights. @@ -30,21 +30,7 @@ #define DBI_MAGIC '~' -/* HvMROMETA introduced in 5.9.5, but mro_meta_init not exported in 5.10.0 */ -#if (PERL_REVISION == 5) -# if (PERL_VERSION < 10) -# define MY_cache_gen(stash) 0 -# else -# if ((PERL_VERSION == 10) && (PERL_SUBVERSION == 0)) -# define MY_cache_gen(stash) \ - (HvAUX(stash)->xhv_mro_meta \ - ? HvAUX(stash)->xhv_mro_meta->cache_gen \ - : 0) -# else -# define MY_cache_gen(stash) HvMROMETA(stash)->cache_gen -# endif -# endif -#endif +#define MY_cache_gen(stash) HvMROMETA(stash)->cache_gen /* If the tests fail with errors about 'setlinebuf' then try */ /* deleting the lines in the block below except the setvbuf one */ @@ -63,13 +49,6 @@ # define DBI_save_hv_fetch_ent # endif -/* prior to 5.8.9: when a CV is duped, the mg dup method is called, - * then *afterwards*, any_ptr is copied from the old CV to the new CV. - * This wipes out anything which the dup method did to any_ptr. - * This needs working around */ -# if defined(USE_ITHREADS) && (PERL_VERSION == 8) && (PERL_SUBVERSION < 9) -# define BROKEN_DUP_ANY_PTR -# endif #endif /* types of method name */ @@ -104,7 +83,7 @@ static void dbih_dumphandle _((pTHX_ SV *h, const char *msg, int level)); static int dbih_dumpcom _((pTHX_ imp_xxh_t *imp_xxh, const char *msg, int level)); static int dbi_ima_free(pTHX_ SV* sv, MAGIC* mg); -#if defined(USE_ITHREADS) && !defined(BROKEN_DUP_ANY_PTR) +#if defined(USE_ITHREADS) static int dbi_ima_dup(pTHX_ MAGIC* mg, CLONE_PARAMS *param); #endif char *neatsvpv _((SV *sv, STRLEN maxlen)); @@ -171,9 +150,6 @@ HV *stash; /* the stash we found the GV in */ GV *gv; /* the GV containing the inner sub */ U32 generation; /* cache invalidation */ -#ifdef BROKEN_DUP_ANY_PTR - PerlInterpreter *my_perl; /* who owns this struct */ -#endif } dbi_ima_t; @@ -200,7 +176,7 @@ (SvOK(state) /* SQLSTATE is implemented by driver */ \ ? (strEQ(SvPV_nolen(state),"00000") ? &PL_sv_no : sv_mortalcopy(state))\ : (SvTRUE(DBIc_ERR(imp_xxh)) \ - ? sv_2mortal(newSVpv("S1000",5)) /* General error */ \ + ? sv_2mortal(newSVpvs("S1000")) /* General error */ \ : &PL_sv_no) /* Success ("00000") */ \ ) @@ -236,7 +212,7 @@ static MGVTBL dbi_ima_vtbl = { 0, 0, 0, 0, dbi_ima_free, 0, -#if defined(USE_ITHREADS) && !defined(BROKEN_DUP_ANY_PTR) +#if defined(USE_ITHREADS) dbi_ima_dup #else 0 @@ -251,17 +227,13 @@ static int dbi_ima_free(pTHX_ SV* sv, PERL_UNUSED_DECL MAGIC* mg) { dbi_ima_t *ima = (dbi_ima_t *)(CvXSUBANY((CV*)sv).any_ptr); -#ifdef BROKEN_DUP_ANY_PTR - if (ima->my_perl != my_perl) - return 0; -#endif SvREFCNT_dec(ima->stash); SvREFCNT_dec(ima->gv); Safefree(ima); return 0; } -#if defined(USE_ITHREADS) && !defined(BROKEN_DUP_ANY_PTR) +#if defined(USE_ITHREADS) static int dbi_ima_dup(pTHX_ MAGIC* mg, CLONE_PARAMS *param) { dbi_ima_t *ima, *nima; @@ -425,7 +397,7 @@ keys = _sort_hash_keys(hash, num_sort, &total_len); if (!keys) - return newSVpv("", 0); + return newSVpvs(""); if (!kv_sep_len) kv_sep_len = strlen(kv_sep); @@ -446,7 +418,7 @@ if (!hash_svp) { /* should never happen */ warn("No hash entry with key '%s'", keys[i]); - sv_catpvn(return_sv, "???", 3); + sv_catpvs(return_sv, "???"); continue; } @@ -457,11 +429,11 @@ if (SvOK(*hash_svp)) { STRLEN hv_val_len; char *hv_val = SvPV(*hash_svp, hv_val_len); - sv_catpvn(return_sv, "'", 1); + sv_catpvs(return_sv, "'"); sv_catpvn(return_sv, hv_val, hv_val_len); - sv_catpvn(return_sv, "'", 1); + sv_catpvs(return_sv, "'"); } - else sv_catpvn(return_sv, "undef", 5); + else sv_catpvs(return_sv, "undef"); } if (i < hv_len-1) @@ -612,14 +584,14 @@ if (SvMAGICAL(sv)) { if (DBIS_TRACE_LEVEL >= 5) { /* add magic details to help debugging */ MAGIC* mg; - infosv = sv_2mortal(newSVpv(" (magic-",0)); - if (SvSMAGICAL(sv)) sv_catpvn(infosv,"s",1); - if (SvGMAGICAL(sv)) sv_catpvn(infosv,"g",1); - if (SvRMAGICAL(sv)) sv_catpvn(infosv,"r",1); - sv_catpvn(infosv,":",1); + infosv = sv_2mortal(newSVpvs(" (magic-")); + if (SvSMAGICAL(sv)) sv_catpvs(infosv, "s"); + if (SvGMAGICAL(sv)) sv_catpvs(infosv, "g"); + if (SvRMAGICAL(sv)) sv_catpvs(infosv, "r"); + sv_catpvs(infosv, ":"); for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) sv_catpvn(infosv, &mg->mg_type, 1); - sv_catpvn(infosv, ")", 1); + sv_catpvs(infosv, ")"); } if (SvGMAGICAL(sv) && !PL_dirty) mg_get(sv); /* trigger magic to FETCH the value */ @@ -689,7 +661,7 @@ SvGROW(nsv, (1+maxlen+1+1)); sv_setpvn(nsv, quote, 1); sv_catpvn(nsv, v, maxlen-3); /* account for three dots */ - sv_catpvn(nsv, "...", 3); + sv_catpvs(nsv, "..."); } else { SvGROW(nsv, (1+len+1+1)); sv_setpvn(nsv, quote, 1); @@ -718,9 +690,9 @@ return; parent = DBIc_PARENT_H(imp_xxh); if (parent && SvROK(parent)) { - SV *tmp_sv = *hv_fetch((HV*)SvRV(h), "Statement", 9, 1); + SV *tmp_sv = *hv_fetchs((HV*)SvRV(h), "Statement", 1); if (SvOK(tmp_sv)) - (void)hv_store((HV*)SvRV(parent), "Statement", 9, SvREFCNT_inc(tmp_sv), 0); + (void)hv_stores((HV*)SvRV(parent), "Statement", SvREFCNT_inc(tmp_sv)); } } @@ -754,7 +726,7 @@ int err_changed = 0; if ( DBIc_has(imp_xxh, DBIcf_HandleSetErr) - && (hook_svp = hv_fetch((HV*)SvRV(h),"HandleSetErr",12,0)) + && (hook_svp = hv_fetchs((HV*)SvRV(h),"HandleSetErr",0)) && hook_svp && ((void)(SvGMAGICAL(*hook_svp) && mg_get(*hook_svp)), SvOK(*hook_svp)) ) { @@ -771,7 +743,7 @@ neatsvpv(method,0) ); PUSHMARK(SP); - XPUSHs(sv_2mortal(newRV_inc((SV*)DBIc_MY_H(imp_xxh)))); + mXPUSHs(newRV_inc((SV*)DBIc_MY_H(imp_xxh))); XPUSHs(err); XPUSHs(errstr); XPUSHs(state); @@ -813,7 +785,7 @@ if (SvTRUE(h_state) && SvTRUE(state) && strNE(SvPV_nolen(h_state), SvPV_nolen(state))) sv_catpvf(h_errstr, " [state was %s now %s]", SvPV_nolen(h_state), SvPV_nolen(state)); if (strNE(SvPV_nolen(h_errstr), SvPV_nolen(errstr))) { - sv_catpvn(h_errstr, "\n", 1); + sv_catpvs(h_errstr, "\n"); sv_catsv(h_errstr, errstr); } } @@ -1107,7 +1079,7 @@ if (!SvMAGICAL(ohv)) { if (!what) return NULL; - if (!hv_fetch((HV*)ohv,"_NO_DESTRUCT_WARN",17,0)) + if (!hv_fetchs((HV*)ohv,"_NO_DESTRUCT_WARN",0)) sv_dump(orv); croak("%s handle %s is not a DBI handle (has no magic)", what, neatsvpv(orv,0)); @@ -1437,7 +1409,7 @@ if ( (imp_mem_stash = gv_stashsv(imp_mem_name, FALSE)) == NULL) croak(errmsg, neatsvpv(orv,0), SvPVbyte_nolen(imp_mem_name), "unknown _mem package"); - if ((svp = hv_fetch((HV*)SvRV(h), "dbi_imp_data", 12, 0))) { + if ((svp = hv_fetchs((HV*)SvRV(h), "dbi_imp_data", 0))) { dbi_imp_data = *svp; if (SvGMAGICAL(dbi_imp_data)) /* call FETCH via magic */ mg_get(dbi_imp_data); @@ -1478,13 +1450,13 @@ /* setup Callbacks from parents' ChildCallbacks */ if (DBIc_has(parent_imp, DBIcf_Callbacks) - && (tmp_svp = hv_fetch((HV*)SvRV(parent), "Callbacks", 9, 0)) + && (tmp_svp = hv_fetchs((HV*)SvRV(parent), "Callbacks", 0)) && SvROK(*tmp_svp) && SvTYPE(SvRV(*tmp_svp)) == SVt_PVHV - && (tmp_svp = hv_fetch((HV*)SvRV(*tmp_svp), "ChildCallbacks", 14, 0)) + && (tmp_svp = hv_fetchs((HV*)SvRV(*tmp_svp), "ChildCallbacks", 0)) && SvROK(*tmp_svp) && SvTYPE(SvRV(*tmp_svp)) == SVt_PVHV ) { /* XXX mirrors behaviour of dbih_set_attr_k() of Callbacks */ - (void)hv_store((HV*)SvRV(h), "Callbacks", 9, newRV_inc(SvRV(*tmp_svp)), 0); + (void)hv_stores((HV*)SvRV(h), "Callbacks", newRV_inc(SvRV(*tmp_svp))); DBIc_set(imp, DBIcf_Callbacks, 1); } @@ -1493,7 +1465,7 @@ if (1) { AV *av; /* add weakref to new (outer) handle into parents ChildHandles array */ - tmp_svp = hv_fetch((HV*)SvRV(parent), "ChildHandles", 12, 1); + tmp_svp = hv_fetchs((HV*)SvRV(parent), "ChildHandles", 1); if (!SvROK(*tmp_svp)) { SV *ChildHandles_rvav = newRV_noinc((SV*)newAV()); sv_setsv(*tmp_svp, ChildHandles_rvav); @@ -1522,16 +1494,16 @@ switch (DBIc_TYPE(imp)) { case DBIt_DB: /* cache _inner_ handle, but also see quick_FETCH */ - (void)hv_store((HV*)SvRV(h), "Driver", 6, newRV_inc(SvRV(parent)), 0); - (void)hv_fetch((HV*)SvRV(h), "Statement", 9, 1); /* store writable undef */ + (void)hv_stores((HV*)SvRV(h), "Driver", newRV_inc(SvRV(parent))); + (void)hv_fetchs((HV*)SvRV(h), "Statement", 1); /* store writable undef */ break; case DBIt_ST: DBIc_NUM_FIELDS((imp_sth_t*)imp) = -1; /* cache _inner_ handle, but also see quick_FETCH */ - (void)hv_store((HV*)SvRV(h), "Database", 8, newRV_inc(SvRV(parent)), 0); + (void)hv_stores((HV*)SvRV(h), "Database", newRV_inc(SvRV(parent))); /* copy (alias) Statement from the sth up into the dbh */ - tmp_svp = hv_fetch((HV*)SvRV(h), "Statement", 9, 1); - (void)hv_store((HV*)SvRV(parent), "Statement", 9, SvREFCNT_inc(*tmp_svp), 0); + tmp_svp = hv_fetchs((HV*)SvRV(h), "Statement", 1); + (void)hv_stores((HV*)SvRV(parent), "Statement", SvREFCNT_inc(*tmp_svp)); break; } } @@ -1586,7 +1558,7 @@ dbih_dumpcom(pTHX_ imp_xxh_t *imp_xxh, const char *msg, int level) { dMY_CXT; - SV *flags = sv_2mortal(newSVpv("",0)); + SV *flags = sv_2mortal(newSVpvs("")); SV *inner; static const char pad[] = " "; if (!msg) @@ -1638,7 +1610,7 @@ if (!inner || !SvROK(inner)) return 1; if (DBIc_TYPE(imp_xxh) <= DBIt_DB) { - SV **svp = hv_fetch((HV*)SvRV(inner), "CachedKids", 10, 0); + SV **svp = hv_fetchs((HV*)SvRV(inner), "CachedKids", 0); if (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) { HV *hv = (HV*)SvRV(*svp); PerlIO_printf(DBILOGFP,"%s CachedKids %d\n", pad, (int)HvKEYS(hv)); @@ -1654,12 +1626,12 @@ } } else if (DBIc_TYPE(imp_xxh) == DBIt_DB) { - SV **svp = hv_fetch((HV*)SvRV(inner), "Name", 4, 0); + SV **svp = hv_fetchs((HV*)SvRV(inner), "Name", 0); if (svp && SvOK(*svp)) PerlIO_printf(DBILOGFP,"%s Name %s\n", pad, neatsvpv(*svp,0)); } else if (DBIc_TYPE(imp_xxh) == DBIt_ST) { - SV **svp = hv_fetch((HV*)SvRV(inner), "Statement", 9, 0); + SV **svp = hv_fetchs((HV*)SvRV(inner), "Statement", 0); if (svp && SvOK(*svp)) PerlIO_printf(DBILOGFP,"%s Statement %s\n", pad, neatsvpv(*svp,0)); } @@ -1834,7 +1806,7 @@ "0", 0, "Number of row fields inconsistent with NUM_OF_FIELDS (driver bug)", "", "_get_fbav"); /* DBIc_NUM_FIELDS(imp_sth) = i; - hv_delete((HV*)SvRV(sth), "NUM_OF_FIELDS", 13, G_DISCARD); + hv_deletes((HV*)SvRV(sth), "NUM_OF_FIELDS", G_DISCARD); */ } /* don't let SvUTF8 flag persist from one row to the next */ @@ -2151,14 +2123,14 @@ dSP; I32 returns; TAINT_NOT; /* the require is presumed innocent till proven guilty */ - perl_require_pv("DBI/Profile.pm"); + require_pv("DBI/Profile.pm"); if (SvTRUE(ERRSV)) { warn("Can't load %s: %s", profile_class, SvPV_nolen(ERRSV)); valuesv = &PL_sv_undef; } else { PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVpv(profile_class,0))); + mXPUSHs(newSVpv(profile_class, 0)); XPUSHs(valuesv); PUTBACK; returns = call_method("_auto_new", G_SCALAR); @@ -2173,7 +2145,7 @@ if (on && !sv_isobject(valuesv)) { /* not blessed already - so default to DBI::Profile */ HV *stash; - perl_require_pv(profile_class); + require_pv(profile_class); stash = gv_stashpv(profile_class, GV_ADDWARN); sv_bless(valuesv, stash); } @@ -2352,7 +2324,7 @@ valuesv = &PL_sv_undef; /* fetch from tied outer handle to trigger FETCH magic */ - svp = hv_fetch((HV*)DBIc_MY_H(imp_sth), "NAME",4, FALSE); + svp = hv_fetchs((HV*)DBIc_MY_H(imp_sth), "NAME", FALSE); sv = (svp) ? *svp : &PL_sv_undef; if (SvGMAGICAL(sv)) /* call FETCH via magic */ mg_get(sv); @@ -2709,7 +2681,8 @@ if (cxix < 0) { break; } - if (PL_DBsub && cxix >= 0 && ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub)) + /* skip the automatic calls to &DB::sub, as perl's pp_caller does */ + if (PL_DBsub && GvCV(PL_DBsub) && cxix >= 0 && ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub)) continue; cx = &ccstack[cxix]; stashname = CopSTASHPV(cx->blk_oldcop); @@ -2752,14 +2725,14 @@ dTHX; dTHR; if (!buf) - buf = sv_2mortal(newSVpv("",0)); + buf = sv_2mortal(newSVpvs("")); else if (!append) sv_setpv(buf,""); if (CopLINE(PL_curcop)) { COP *cop; dbi_caller_string(buf, PL_curcop, prefix, show_line, show_path); if (show_caller && (cop = dbi_caller_cop())) { - SV *via = sv_2mortal(newSVpv("",0)); + SV *via = sv_2mortal(newSVpvs("")); dbi_caller_string(via, cop, prefix, show_line, show_path); sv_catpvf(buf, " via %s", SvPV_nolen(via)); } @@ -2776,7 +2749,7 @@ clear_cached_kids(pTHX_ SV *h, imp_xxh_t *imp_xxh, const char *meth_name, int trace_level) { if (DBIc_TYPE(imp_xxh) <= DBIt_DB) { - SV **svp = hv_fetch((HV*)SvRV(h), "CachedKids", 10, 0); + SV **svp = hv_fetchs((HV*)SvRV(h), "CachedKids", 0); if (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) { HV *hv = (HV*)SvRV(*svp); if (HvKEYS(hv)) { @@ -2890,7 +2863,7 @@ h_hv = (HV*)SvRV(dbih_inner(aTHX_ h, "dbi_profile")); - profile = *hv_fetch(h_hv, "Profile", 7, 1); + profile = *hv_fetchs(h_hv, "Profile", 1); if (profile && SvMAGICAL(profile)) mg_get(profile); /* FETCH */ if (!profile || !SvROK(profile)) { @@ -2907,7 +2880,7 @@ /* statement_sv: undef = use $h->{Statement}, "" (&sv_no) = use empty string */ if (!SvOK(statement_sv)) { - SV **psv = hv_fetch(h_hv, "Statement", 9, 0); + SV **psv = hv_fetchs(h_hv, "Statement", 0); statement_sv = (psv && SvOK(*psv)) ? *psv : &PL_sv_no; } statement_pv = SvPV_nolen(statement_sv); @@ -2918,7 +2891,7 @@ dest_node = _profile_next_node(profile, "Data"); - tmp = *hv_fetch((HV*)SvRV(profile), "Path", 4, 1); + tmp = *hv_fetchs((HV*)SvRV(profile), "Path", 1); if (SvROK(tmp) && SvTYPE(SvRV(tmp))==SVt_PVAV) { int len; av = (AV*)SvRV(tmp); @@ -2938,7 +2911,7 @@ EXTEND(SP, 4); PUSHMARK(SP); PUSHs(h); /* push inner handle, then others params */ - PUSHs( sv_2mortal(newSVpv(method_pv,0))); + mPUSHs(newSVpv(method_pv, 0)); PUTBACK; SAVE_DEFSV; /* local($_) = $statement */ DEFSV_set(statement_sv); @@ -2983,7 +2956,7 @@ else if (isGV(method)) { /* just using SvPV_nolen(method) sometimes causes an error: */ /* "Can't coerce GLOB to string" so we use gv_efullname() */ - SV *tmpsv = sv_2mortal(newSVpv("",0)); + SV *tmpsv = sv_2mortal(newSVpvs("")); gv_efullname4(tmpsv, (GV*)method, "", TRUE); p = SvPV_nolen(tmpsv); if (*p == '*') ++p; /* skip past leading '*' glob sigil */ @@ -3217,20 +3190,6 @@ SV *qsv = Nullsv; /* quick result from a shortcut method */ -#ifdef BROKEN_DUP_ANY_PTR - if (ima->my_perl != my_perl) { - /* we couldn't dup the ima struct at clone time, so do it now */ - dbi_ima_t *nima; - Newx(nima, 1, dbi_ima_t); - *nima = *ima; /* structure copy */ - CvXSUBANY(cv).any_ptr = nima; - nima->stash = NULL; - nima->gv = NULL; - nima->my_perl = my_perl; - ima = nima; - } -#endif - ima_flags = ima->flags; meth_type = ima->meth_type; if (trace_level >= 9) { @@ -3415,7 +3374,7 @@ keep_error = TRUE; if (ima_flags & IMA_CLEAR_STMT) { /* don't use SvOK_off: dbh's Statement may be ref to sth's */ - (void)hv_store((HV*)SvRV(h), "Statement", 9, &PL_sv_undef, 0); + (void)hv_stores((HV*)SvRV(h), "Statement", &PL_sv_undef); } if (ima_flags & IMA_CLEAR_CACHED_KIDS) clear_cached_kids(aTHX_ h, imp_xxh, meth_name, trace_flags); @@ -3423,19 +3382,17 @@ } if (ima_flags & IMA_HAS_USAGE) { - const char *err = NULL; - char msg[200]; + SV *usage_err = Nullsv; if (ima->minargs && (items < ima->minargs || (ima->maxargs>0 && items > ima->maxargs))) { - sprintf(msg, + usage_err = sv_2mortal(newSVpvf( "DBI %s: invalid number of arguments: got handle + %ld, expected handle + between %d and %d\n", - meth_name, (long)items-1, (int)ima->minargs-1, (int)ima->maxargs-1); - err = msg; + meth_name, (long)items-1, (int)ima->minargs-1, (int)ima->maxargs-1)); } /* arg type checking could be added here later */ - if (err) { - croak("%sUsage: %s->%s(%s)", err, "$h", meth_name, + if (usage_err) { + croak("%sUsage: %s->%s(%s)", SvPV_nolen(usage_err), "$h", meth_name, (ima->usage_msg) ? ima->usage_msg : "...?"); } } @@ -3451,11 +3408,11 @@ ) { for(i=1; i < items; ++i) { if (SvTAINTED(ST(i))) { - char buf[100]; - sprintf(buf,"parameter %d of %s->%s method call", - i, SvPV_nolen(h), meth_name); + SV *taint_msg = sv_2mortal(newSVpvf( + "parameter %d of %s->%s method call", + i, SvPV_nolen(h), meth_name)); PL_tainted = 1; /* needed for TAINT_PROPER to work */ - TAINT_PROPER(buf); /* die's */ + TAINT_PROPER(SvPV_nolen(taint_msg)); /* die's */ } } } @@ -3464,7 +3421,7 @@ if (is_DESTROY) { /* force destruction of any outstanding children */ - if ((tmp_svp = hv_fetch((HV*)SvRV(h), "ChildHandles", 12, FALSE)) && SvROK(*tmp_svp)) { + if ((tmp_svp = hv_fetchs((HV*)SvRV(h), "ChildHandles", FALSE)) && SvROK(*tmp_svp)) { AV *av = (AV*)SvRV(*tmp_svp); I32 kidslots; PerlIO *logfp = DBILOGFP; @@ -3556,7 +3513,7 @@ } if (DBIc_has(imp_xxh,DBIcf_Callbacks) - && (tmp_svp = hv_fetch((HV*)SvRV(h), "Callbacks", 9, 0)) + && (tmp_svp = hv_fetchs((HV*)SvRV(h), "Callbacks", 0)) && ( (hook_svp = hv_fetch((HV*)SvRV(*tmp_svp), meth_name, strlen(meth_name), 0)) /* the "*" fallback callback only applies to non-nested calls * and also doesn't apply to the 'set_err' or DESTROY methods. @@ -3566,7 +3523,7 @@ */ || (!is_nested_call && !PL_dirty && meth_type != methtype_set_err && meth_type != methtype_DESTROY && - (hook_svp = hv_fetch((HV*)SvRV(*tmp_svp), "*", 1, 0)) + (hook_svp = hv_fetchs((HV*)SvRV(*tmp_svp), "*", 0)) ) ) && SvROK(*hook_svp) @@ -3719,7 +3676,7 @@ imp_msv = (SV*)gv_fetchmethod_autoload(DBIc_IMP_STASH(imp_xxh), "func", FALSE); if (imp_msv) { /* driver does have func method so undo the earlier 'func' stack changes */ - PUSHs(sv_2mortal(newSVpv(meth_name,0))); + mPUSHs(newSVpv(meth_name, 0)); PUTBACK; ++items; meth_name = "func"; @@ -3925,7 +3882,7 @@ /* and may mess up the error handling below for the commit/rollback */ PUSHMARK(SP); XPUSHs(h); - XPUSHs(sv_2mortal(newSVpv("AutoCommit",0))); + mXPUSHs(newSVpvs("AutoCommit")); XPUSHs(&PL_sv_yes); PUTBACK; call_method("STORE", G_VOID); @@ -4000,18 +3957,15 @@ SV **statement_svp = NULL; const int is_warning = (!SvTRUE(err_sv) && strlen(SvPV_nolen(err_sv))==1); const char *err_meth_name = meth_name; - char intro[200]; if (meth_type == methtype_set_err) { - SV **sem_svp = hv_fetch((HV*)SvRV(h), "dbi_set_err_method", 18, GV_ADDWARN); + SV **sem_svp = hv_fetchs((HV*)SvRV(h), "dbi_set_err_method", GV_ADDWARN); if (SvOK(*sem_svp)) err_meth_name = SvPV_nolen(*sem_svp); } - /* XXX change to vsprintf into sv directly */ - sprintf(intro,"%s %s %s: ", HvNAME(DBIc_IMP_STASH(imp_xxh)), err_meth_name, - SvTRUE(err_sv) ? "failed" : is_warning ? "warning" : "information"); - msg = sv_2mortal(newSVpv(intro,0)); + msg = sv_2mortal(newSVpvf("%s %s %s: ", HvNAME(DBIc_IMP_STASH(imp_xxh)), err_meth_name, + SvTRUE(err_sv) ? "failed" : is_warning ? "warning" : "information")); if (SvOK(DBIc_ERRSTR(imp_xxh))) sv_catsv(msg, DBIc_ERRSTR(imp_xxh)); else @@ -4021,7 +3975,7 @@ if ( DBIc_has(imp_xxh, DBIcf_ShowErrorStatement) && !is_unrelated_to_Statement && (DBIc_TYPE(imp_xxh) == DBIt_ST || ima_flags & IMA_SHOW_ERR_STMT) - && (statement_svp = hv_fetch((HV*)SvRV(h), "Statement", 9, 0)) + && (statement_svp = hv_fetchs((HV*)SvRV(h), "Statement", 0)) && statement_svp && SvOK(*statement_svp) ) { SV **svp = 0; @@ -4031,7 +3985,7 @@ /* fetch from tied outer handle to trigger FETCH magic */ /* could add DBIcf_ShowErrorParams (default to on?) */ if (!(ima_flags & IMA_HIDE_ERR_PARAMVALUES)) { - svp = hv_fetch((HV*)DBIc_MY_H(imp_xxh),"ParamValues",11,FALSE); + svp = hv_fetchs((HV*)DBIc_MY_H(imp_xxh),"ParamValues",FALSE); if (svp && SvMAGICAL(*svp)) mg_get(*svp); /* XXX may recurse, may croak. could use eval */ } @@ -4056,7 +4010,7 @@ hook_svp = NULL; if ( (SvTRUE(err_sv) || (is_warning && DBIc_has(imp_xxh, DBIcf_RaiseWarn))) && DBIc_has(imp_xxh, DBIcf_HandleError) - && (hook_svp = hv_fetch((HV*)SvRV(h),"HandleError",11,0)) + && (hook_svp = hv_fetchs((HV*)SvRV(h),"HandleError",0)) && hook_svp && SvOK(*hook_svp) ) { dSP; @@ -4082,7 +4036,7 @@ ); PUSHMARK(SP); XPUSHs(msg); - XPUSHs(sv_2mortal(newRV_inc((SV*)DBIc_MY_H(imp_xxh)))); + mXPUSHs(newRV_inc((SV*)DBIc_MY_H(imp_xxh))); XPUSHs( result ); PUTBACK; items = call_sv(*hook_svp, G_SCALAR); @@ -4190,6 +4144,7 @@ we add support for odbc escape sequences. */ int idx = 1; + int sln; char in_quote = '\0'; char in_comment = '\0'; @@ -4206,7 +4161,21 @@ } /* XXX this allocation strategy won't work when we get to more advanced stuff */ - new_stmt_sv = newSV(strlen(statement) * 3); + /* The 7 is for length increase from '?' (length 1) to :p99999 (length 7) + * which imposes a limit of 99999 '?' placeholders POSIX style. Actual counts + * are a bit higher: + * using factor 5: :p1 .. :p1107 + * using factor 6: :p1 .. :p11106 + * using factor 7: :p1 .. :p111105 + * and that count is insane already */ + sln = strlen(statement); + if (sln > 306783375) { /* x * 7 + 16 would overflow maxint on 32bit ints */ + char buf[99]; + sprintf(buf, "preparse statement length exceeds maximum length (306783375)."); + set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); + return &PL_sv_undef; + } + new_stmt_sv = newSV(sln * 7 + 16); sv_setpv(new_stmt_sv,""); src = statement; dest = SvPVX(new_stmt_sv); @@ -4235,7 +4204,13 @@ } if (in_comment == '/') src++; - src += (*src != '\n' || *(dest-1)=='\n') ? 1 : 0; + /* Only inspect the previously-emitted byte if one exists; + when an initial line comment is deleted, dest is still at + the start of the output buffer and *(dest-1) would read + one byte before it (OOB read). */ + src += (*src != '\n' + || (dest > SvPVX(new_stmt_sv) && *(dest-1)=='\n')) + ? 1 : 0; in_comment = '\0'; rt_comment = '\0'; } @@ -4345,9 +4320,9 @@ continue; } - if ( !(*src==':' && (PS_accept(DBIpp_ph_cn) || PS_accept(DBIpp_ph_cs))) - && !(*src=='?' && PS_accept(DBIpp_ph_qm)) - ){ + if ( !(*src==':' && (PS_accept(DBIpp_ph_cn) || PS_accept(DBIpp_ph_cs))) + && !(*src=='?' && PS_accept(DBIpp_ph_qm)) + ){ if (*src == '\'' || *src == '"') in_quote = *src; *dest++ = *src++; @@ -4366,37 +4341,51 @@ if (PS_return(DBIpp_ph_qm)) ; else if (PS_return(DBIpp_ph_cn)) { /* '?' -> ':p1' (etc) */ + if (idx >= 99999) { + char buf[99]; + sprintf(buf, "preparse found more than 99999 '?' placeholders. Limit exceeded."); + set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); + return &PL_sv_undef; + } sprintf(start,":p%d", idx++); dest = start+strlen(start); } else if (PS_return(DBIpp_ph_sp)) { /* '?' -> '%s' */ - *start = '%'; - *dest++ = 's'; + *start = '%'; + *dest++ = 's'; } } else if (isDIGIT(*src)) { /* :1 */ const int pln = atoi(src); + + if (pln > 99999 || pln <= 0) { + char buf[99]; + sprintf(buf, "preparse found :p%d which is outside the allowed range.", pln); + set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); + return &PL_sv_undef; + } + style = ":1"; - if (PS_return(DBIpp_ph_cn)) { /* ':1'->':p1' */ - idx = pln; - *dest++ = 'p'; - while(isDIGIT(*src)) - *dest++ = *src++; + if (PS_return(DBIpp_ph_cn)) { /* ':1'-> ':p1' */ + idx = pln; + *dest++ = 'p'; + while(isDIGIT(*src)) + *dest++ = *src++; } else if (PS_return(DBIpp_ph_qm) /* ':1' -> '?' */ || PS_return(DBIpp_ph_sp) /* ':1' -> '%s' */ ) { - PS_return(DBIpp_ph_qm) ? sprintf(start,"?") : sprintf(start,"%%s"); - dest = start + strlen(start); - if (pln != idx) { - char buf[99]; - sprintf(buf, "preparse found placeholder :%d out of sequence, expected :%d", pln, idx); - set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); - return &PL_sv_undef; - } - while(isDIGIT(*src)) src++; - idx++; + PS_return(DBIpp_ph_qm) ? sprintf(start,"?") : sprintf(start,"%%s"); + dest = start + strlen(start); + if (pln != idx) { + char buf[99]; + sprintf(buf, "preparse found placeholder :%d out of sequence, expected :%d", pln, idx); + set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); + return &PL_sv_undef; + } + while(isDIGIT(*src)) src++; + idx++; } } else if (isALNUM(*src)) /* :name */ @@ -4605,7 +4594,7 @@ PERL_UNUSED_VAR(cv); } - (void)hv_store((HV*)SvRV(attr_ref), "ImplementorClass", 16, SvREFCNT_inc(imp_class), 0); + (void)hv_stores((HV*)SvRV(attr_ref), "ImplementorClass", SvREFCNT_inc(imp_class)); /* make attr into inner handle by blessing it into class */ sv_bless(attr_ref, class_stash); @@ -4711,7 +4700,7 @@ { dMY_CXT; /* install another method name/interface for the DBI dispatcher */ - SV *trace_msg = (DBIS_TRACE_LEVEL >= 10) ? sv_2mortal(newSVpv("",0)) : Nullsv; + SV *trace_msg = (DBIS_TRACE_LEVEL >= 10) ? sv_2mortal(newSVpvs("")) : Nullsv; CV *cv; SV **svp; dbi_ima_t *ima; @@ -4767,11 +4756,7 @@ * pointer to the mg, but not the SV) */ mg = sv_magicext((SV*)cv, NULL, DBI_MAGIC, &dbi_ima_vtbl, (char *)cv, 0); -#ifdef BROKEN_DUP_ANY_PTR - ima->my_perl = my_perl; /* who owns this struct */ -#else mg->mg_flags |= MGf_DUP; -#endif ST(0) = &PL_sv_yes; } @@ -5126,7 +5111,7 @@ * destroyed they may need to interact with the 'zombie' parent dbh. * So we do our best to neautralize them (finish & rebless) */ - if ((tmp_svp = hv_fetch((HV*)SvRV(h), "ChildHandles", 12, FALSE)) && SvROK(*tmp_svp)) { + if ((tmp_svp = hv_fetchs((HV*)SvRV(h), "ChildHandles", FALSE)) && SvROK(*tmp_svp)) { AV *av = (AV*)SvRV(*tmp_svp); HV *zombie_stash = gv_stashpv("DBI::zombie", GV_ADDWARN); I32 kidslots; @@ -5210,6 +5195,9 @@ src_av = (AV*)SvRV(src_rv); src_fields = AvFILL(src_av)+1; if (src_fields != dst_fields) { + if ( dst_fields == 0 ) + croak("_set_fbav(%s): array has %d elements, but the statement handle row buffer has 0", + neatsvpv(src_rv,0), src_fields); warn("_set_fbav(%s): array has %d elements, the statement handle row buffer has %d (and NUM_OF_FIELDS is %d)", neatsvpv(src_rv,0), src_fields, dst_fields, DBIc_NUM_FIELDS(imp_sth)); SvREADONLY_off(dst_av); @@ -5570,14 +5558,13 @@ } else { /* store provided method name so handler code can find it */ - sem_svp = hv_fetch((HV*)SvRV(h), "dbi_set_err_method", 18, 1); + sem_svp = hv_fetchs((HV*)SvRV(h), "dbi_set_err_method", 1); if (SvOK(method)) { sv_setpv(*sem_svp, SvPV_nolen(method)); } else (void)SvOK_off(*sem_svp); - EXTEND(SP, 1); - PUSHs( result ? result : &PL_sv_undef ); + XPUSHs( result ? result : &PL_sv_undef ); } /* We don't check RaiseError and call die here because that must be */ /* done by returning through dispatch and letting the DBI handle it */ diff -Nru libdbi-perl-1.647/DBIXS.h libdbi-perl-1.652/DBIXS.h --- libdbi-perl-1.647/DBIXS.h 2025-01-09 14:39:14.000000000 +0000 +++ libdbi-perl-1.652/DBIXS.h 2026-08-03 09:41:36.000000000 +0000 @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (c) 2024-2025 DBI Team + * Copyright (c) 2024-2026 DBI Team * Copyright (c) 1994-2024 Tim Bunce Ireland * * See COPYRIGHT section in DBI.pm for usage and distribution rights. @@ -12,6 +12,9 @@ #ifndef DBIXS_VERSION /* prevent multiple inclusion */ +/* define DBIXS_VERSION & DBIXS_REVISION */ +#include "dbixs_rev.h" + #ifndef DBIS #define DBIS dbis /* default name for dbistate_t variable */ #endif @@ -32,9 +35,6 @@ #undef std #endif -/* define DBIXS_REVISION */ -#include "dbixs_rev.h" - /* Perl backwards compatibility definitions */ #define NEED_sv_2pv_flags #define NEED_croak_xs_usage @@ -43,9 +43,6 @@ /* DBI SQL_* type definitions */ #include "dbi_sql.h" - -#define DBIXS_VERSION 93 /* superseded by DBIXS_REVISION */ - #ifdef NEED_DBIXS_VERSION #if NEED_DBIXS_VERSION > DBIXS_VERSION error You_need_to_upgrade_your_DBI_module_before_building_this_driver @@ -54,7 +51,6 @@ #define NEED_DBIXS_VERSION DBIXS_VERSION #endif - #define DBI_LOCK #define DBI_UNLOCK @@ -244,8 +240,10 @@ ( (flags && (DBIc_TRACE_FLAGS(imp) & flags) && (DBIc_TRACE_LEVEL(imp) >= flaglevel)) \ || (level && DBIc_TRACE_LEVEL(imp) >= level) ) +/* Deprecated, but cannot be removed, becaused used in e.g. DBD::Oracle :( */ #define DBIc_DEBUG(imp) (_imp2com(imp, attr.TraceLevel)) /* deprecated */ #define DBIc_DEBUGIV(imp) SvIV(DBIc_DEBUG(imp)) /* deprecated */ + #define DBIc_STATE(imp) SvRV(_imp2com(imp, attr.State)) #define DBIc_ERR(imp) SvRV(_imp2com(imp, attr.Err)) #define DBIc_ERRSTR(imp) SvRV(_imp2com(imp, attr.Errstr)) @@ -497,7 +495,7 @@ CV *cv = get_cv("DBI::_dbi_state_lval", 0); \ if (!cv) \ croak("Unable to get DBI state function. DBI not loaded."); \ - dbi_state_lval_p = (_dbi_state_lval_t)CvXSUB(cv); \ + dbi_state_lval_p = (_dbi_state_lval_t)(void *)CvXSUB(cv); \ } \ return dbi_state_lval_p(aTHX); \ } \ diff -Nru libdbi-perl-1.647/Driver.xst libdbi-perl-1.652/Driver.xst --- libdbi-perl-1.647/Driver.xst 2025-01-09 14:39:44.000000000 +0000 +++ libdbi-perl-1.652/Driver.xst 2026-05-28 12:03:05.000000000 +0000 @@ -1,5 +1,5 @@ # $Id$ -# Copyright (c) 2024-2025 DBI Team +# Copyright (c) 2024-2026 DBI Team # Copyright (c) 1997-2024 Tim Bunce Ireland # Copyright (c) 2002 Jonathan Leffler # @@ -421,8 +421,8 @@ && (!PL_dirty || DBIc_DBISTATE(imp_dbh)->debug >= 3) ) { warn("Issuing rollback() due to DESTROY without explicit disconnect() of %s handle %s", - SvPV_nolen(*hv_fetch((HV*)SvRV(dbh), "ImplementorClass", 16, 1)), - SvPV_nolen(*hv_fetch((HV*)SvRV(dbh), "Name", 4, 1)) + SvPV_nolen(*hv_fetchs((HV*)SvRV(dbh), "ImplementorClass", 1)), + SvPV_nolen(*hv_fetchs((HV*)SvRV(dbh), "Name", 1)) ); } dbd_db_rollback(dbh, imp_dbh); /* ROLLBACK! */ diff -Nru libdbi-perl-1.647/Driver_xst.h libdbi-perl-1.652/Driver_xst.h --- libdbi-perl-1.647/Driver_xst.h 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/Driver_xst.h 2026-08-03 09:41:36.000000000 +0000 @@ -62,7 +62,7 @@ ) { char errmsg[99]; /* clear any previous ParamValues before error is generated */ - SV **svp = hv_fetch((HV*)DBIc_MY_H(imp_sth),"ParamValues",11,FALSE); + SV **svp = hv_fetchs((HV*)DBIc_MY_H(imp_sth),"ParamValues",FALSE); if (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) { HV *hv = (HV*)SvRV(*svp); hv_clear(hv); diff -Nru libdbi-perl-1.647/LICENSE libdbi-perl-1.652/LICENSE --- libdbi-perl-1.647/LICENSE 2025-01-09 14:39:55.000000000 +0000 +++ libdbi-perl-1.652/LICENSE 2026-05-28 12:03:11.000000000 +0000 @@ -1,4 +1,4 @@ -DBI is Copyright (c) 1994-2025 by Tim Bunce, the DBI Team and others. +DBI is Copyright (c) 1994-2026 by Tim Bunce, the DBI Team and others. See LICENSE included with this distribution. All rights reserved. This is free software; you can redistribute it and/or modify it under the diff -Nru libdbi-perl-1.647/MANIFEST libdbi-perl-1.652/MANIFEST --- libdbi-perl-1.647/MANIFEST 2025-01-20 08:13:30.000000000 +0000 +++ libdbi-perl-1.652/MANIFEST 2026-08-15 11:29:17.000000000 +0000 @@ -1,47 +1,43 @@ ChangeLog History of significant changes to the DBI -DBI.pm The Database Interface Module Perl code -DBI.xs The Database Interface Module XS code -DBIXS.h The DBI XS public interface for Drivers (DBD::...) -Driver.xst Template driver xs file -Driver_xst.h Template driver xs support code -INSTALL -LICENSE -MANIFEST -Makefile.PL The Makefile generator +CONTRIBUTING.md cpanfile -Perl.xs Test harness (currently) for Driver.xst -README.md dbd_xsh.h Prototypes for standard Driver.xst interface +DBI.pm The Database Interface Module Perl code +DBI.xs The Database Interface Module XS code dbi_sql.h Definitions based on SQL CLI / ODBC (#inc'd by DBIXS.h) +dbilogstrip.PL Utility to normalise DBI logs so they can be compared with diff dbipport.h Perl portability macros (from Devel::PPort) -dbilogstrip.PL Utility to normalise DBI logs so they can be compared with diff dbiprof.PL dbiproxy.PL Frontend for DBI::ProxyServer dbivport.h DBI version portability macros (for drivers to copy) +DBIXS.h The DBI XS public interface for Drivers (DBD::...) dbixs_rev.h Defines DBIXS_REVISION macro holding DBIXS.h subversion revision number dbixs_rev.pl Utility to write dbixs_rev.h +Driver.xst Template driver xs file +Driver_xst.h Template driver xs support code +ex/corogofer.pl A test script for DBD::Gofer::Transport::corostream ex/perl_dbi_nulls_test.pl A test script for forms of IS NULL qualification in SQL -ex/profile.pl A test script for DBI::Profile -ex/corogofer.pl A test script for DBD::Gofer::Transport::corostream +ex/profile.pl A test script for DBI::Profile ex/unicode_test.pl +INSTALL lib/Bundle/DBI.pm A bundle for automatic installation via CPAN. lib/DBD/DBM.pm A driver for DBM files (uses DBD::File) lib/DBD/ExampleP.pm A very simple example Driver module lib/DBD/File.pm A driver base class for simple drivers lib/DBD/File/Developers.pod Developer documentation for DBD::File -lib/DBD/File/Roadmap.pod Roadmap for DBD::File and other Pure Perl DBD's lib/DBD/File/HowTo.pod Guide to write a DBD::File based DBI driver -lib/DBD/Gofer.pm DBD::Gofer 'stateless proxy' driver +lib/DBD/File/Roadmap.pod Roadmap for DBD::File and other Pure Perl DBD's +lib/DBD/Gofer.pm DBD::Gofer 'stateless proxy' driver lib/DBD/Gofer/Policy/Base.pm -lib/DBD/Gofer/Policy/pedantic.pm Safest and most transparent, but also slowest -lib/DBD/Gofer/Policy/classic.pm Reasonable policy for typical usage -lib/DBD/Gofer/Policy/rush.pm Raw speed, fewest round trips, least transparent -lib/DBD/Gofer/Transport/Base.pm Base class for DBD::Gofer driver transport classes -lib/DBD/Gofer/Transport/corostream.pm Async Gofer transport using Coro and AnyEvent -lib/DBD/Gofer/Transport/null.pm DBD::Gofer transport that executes in same process (for testing) -lib/DBD/Gofer/Transport/pipeone.pm DBD::Gofer transport to new subprocess for each request -lib/DBD/Gofer/Transport/stream.pm DBD::Gofer transport for ssh etc -lib/DBD/Mem.pm A pure-perl in-memory driver using DBI::DBD::SqlEngine +lib/DBD/Gofer/Policy/classic.pm Reasonable policy for typical usage +lib/DBD/Gofer/Policy/pedantic.pm Safest and most transparent, but also slowest +lib/DBD/Gofer/Policy/rush.pm Raw speed, fewest round trips, least transparent +lib/DBD/Gofer/Transport/Base.pm Base class for DBD::Gofer driver transport classes +lib/DBD/Gofer/Transport/corostream.pm Async Gofer transport using Coro and AnyEvent +lib/DBD/Gofer/Transport/null.pm DBD::Gofer transport that executes in same process (for testing) +lib/DBD/Gofer/Transport/pipeone.pm DBD::Gofer transport to new subprocess for each request +lib/DBD/Gofer/Transport/stream.pm DBD::Gofer transport for ssh etc +lib/DBD/Mem.pm A pure-perl in-memory driver using DBI::DBD::SqlEngine lib/DBD/NullP.pm An empty example Driver module lib/DBD/Proxy.pm Proxy driver lib/DBD/Sponge.pm A driver for fake cursors (precached data) @@ -55,15 +51,15 @@ lib/DBI/DBD/SqlEngine.pm SQL Engine for drivers without an own lib/DBI/DBD/SqlEngine/Developers.pod DBI::DBD::SqlEngine API Documentation lib/DBI/DBD/SqlEngine/HowTo.pod HowTo ... write a DBI::DBD::SqlEngine based driver -lib/DBI/Gofer/Execute.pm Execution logic for DBD::Gofer server -lib/DBI/Gofer/Request.pm Request object from DBD::Gofer -lib/DBI/Gofer/Response.pm Response object for DBD::Gofer +lib/DBI/Gofer/Execute.pm Execution logic for DBD::Gofer server +lib/DBI/Gofer/Request.pm Request object from DBD::Gofer +lib/DBI/Gofer/Response.pm Response object for DBD::Gofer lib/DBI/Gofer/Serializer/Base.pm lib/DBI/Gofer/Serializer/DataDumper.pm lib/DBI/Gofer/Serializer/Storable.pm -lib/DBI/Gofer/Transport/Base.pm Base class for DBD::Gofer server transport classes -lib/DBI/Gofer/Transport/pipeone.pm DBD::Gofer transport for single requests -lib/DBI/Gofer/Transport/stream.pm DBI::Gofer transport for ssh etc +lib/DBI/Gofer/Transport/Base.pm Base class for DBD::Gofer server transport classes +lib/DBI/Gofer/Transport/pipeone.pm DBD::Gofer transport for single requests +lib/DBI/Gofer/Transport/stream.pm DBI::Gofer transport for ssh etc lib/DBI/Profile.pm Manage DBI usage profile data lib/DBI/ProfileData.pm lib/DBI/ProfileDumper.pm @@ -72,10 +68,16 @@ lib/DBI/ProxyServer.pm The proxy drivers server lib/DBI/PurePerl.pm A DBI.xs emulation in Perl lib/DBI/SQL/Nano.pm A 'smaller than micro' SQL parser -lib/DBI/Util/_accessor.pm A very cut-down version of Class::Accessor::Fast -lib/DBI/Util/CacheMemory.pm A very cut-down version of Cache::Memory +lib/DBI/Util/_accessor.pm A very cut-down version of Class::Accessor::Fast +lib/DBI/Util/CacheMemory.pm A very cut-down version of Cache::Memory lib/DBI/W32ODBC.pm An experimental DBI emulation layer for Win32::ODBC lib/Win32/DBIODBC.pm An experimental Win32::ODBC emulation layer for DBI +LICENSE +Makefile.PL The Makefile generator +MANIFEST +Perl.xs Test harness (currently) for Driver.xst +README.md +SECURITY.md t/01basics.t t/02dbidrv.t t/03handle.t @@ -102,7 +104,7 @@ t/41prof_dump.t t/42prof_data.t t/43prof_env.t -t/48dbi_dbd_sqlengine.t Tests for DBI::DBD::SqlEngine +t/48dbi_dbd_sqlengine.t Tests for DBI::DBD::SqlEngine t/49dbd_file.t DBD::File API and very basic tests t/50dbm_simple.t simple DBD::DBM tests t/51dbm_file.t extended DBD::File tests (through DBD::DBM) @@ -115,12 +117,13 @@ t/72childhandles.t t/73cachedkids.t t/80proxy.t +t/82sponge.t t/85gofer.t t/86gofer_fail.t t/87gofer_cache.t t/90sql_type_cast.t t/91_store_warning.t -t/lib.pl Utility functions for test scripts +t/lib.pl Utility functions for test scripts typemap META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) diff -Nru libdbi-perl-1.647/META.json libdbi-perl-1.652/META.json --- libdbi-perl-1.647/META.json 2025-01-20 08:13:30.000000000 +0000 +++ libdbi-perl-1.652/META.json 2026-08-15 11:29:17.000000000 +0000 @@ -2,64 +2,66 @@ "abstract" : "Database independent interface for Perl", "author" : [ "DBI team (dbi-users@perl.org)" - ], + ], "dynamic_config" : 1, - "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", + "generated_by" : "ExtUtils::MakeMaker version 7.78, CPAN::Meta::Converter version 2.150013", "license" : [ "perl_5" - ], + ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 - }, + }, "name" : "DBI", "no_index" : { "directory" : [ "t", "inc" - ] - }, + ] + }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "6.48", - "Test::Simple" : "0.90" - } - }, + "Test::Simple" : "0.96" + } + }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" - } - }, + } + }, "runtime" : { "requires" : { - "perl" : "5.008001" + "Module::Load" : "0.22", + "perl" : "5.012000" + } } - } - }, + }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/perl5-dbi/dbi/issues" - }, + }, "homepage" : "http://dbi.perl.org/", "license" : [ "http://dev.perl.org/licenses/" - ], + ], "repository" : { "url" : "https://github.com/perl5-dbi/dbi" - }, + }, "x_IRC" : "irc://irc.perl.org/#dbi", "x_MailingList" : "mailto:dbi-dev@perl.org" - }, - "version" : "1.647", - "x_serialization_backend" : "JSON::PP version 4.16", + }, + "version" : "1.652", + "x_serialization_backend" : "JSON::PP version 4.18", "x_suggests" : { "Clone" : 0.47, "DB_File" : 0, "MLDBM" : 0, "Net::Daemon" : 0, + "RPC::PlClient" : 0.202, "RPC::PlServer" : 0.202, "SQL::Statement" : 1.414 + } } -} diff -Nru libdbi-perl-1.647/META.yml libdbi-perl-1.652/META.yml --- libdbi-perl-1.647/META.yml 2025-01-20 08:13:30.000000000 +0000 +++ libdbi-perl-1.652/META.yml 2026-08-15 11:29:17.000000000 +0000 @@ -4,11 +4,11 @@ - 'DBI team (dbi-users@perl.org)' build_requires: ExtUtils::MakeMaker: '6.48' - Test::Simple: '0.90' + Test::Simple: '0.96' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 -generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' +generated_by: 'ExtUtils::MakeMaker version 7.78, CPAN::Meta::Converter version 2.150013' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -19,7 +19,8 @@ - t - inc requires: - perl: '5.008001' + Module::Load: '0.22' + perl: '5.012000' resources: IRC: irc://irc.perl.org/#dbi MailingList: mailto:dbi-dev@perl.org @@ -27,12 +28,13 @@ homepage: http://dbi.perl.org/ license: http://dev.perl.org/licenses/ repository: https://github.com/perl5-dbi/dbi -version: '1.647' +version: '1.652' x_serialization_backend: 'CPAN::Meta::YAML version 0.020' x_suggests: Clone: 0.47 DB_File: 0 MLDBM: 0 Net::Daemon: 0 + RPC::PlClient: 0.202 RPC::PlServer: 0.202 SQL::Statement: 1.414 diff -Nru libdbi-perl-1.647/Makefile.PL libdbi-perl-1.652/Makefile.PL --- libdbi-perl-1.647/Makefile.PL 2025-01-20 08:10:33.000000000 +0000 +++ libdbi-perl-1.652/Makefile.PL 2026-07-26 13:36:58.000000000 +0000 @@ -3,12 +3,12 @@ # # $Id$ # -# Copyright (c) 2024-2025 DBI Team +# Copyright (c) 2024-2026 DBI Team # Copyright (c) 1994-2024 Tim Bunce Ireland # # See COPYRIGHT section in DBI.pm for usage and distribution rights. -use 5.008_001; +use 5.012_000; use ExtUtils::MakeMaker 5.16, qw(WriteMakefile $Verbose prompt); use Getopt::Long; @@ -59,10 +59,13 @@ AUTHOR => 'DBI team (dbi-users@perl.org)', VERSION_FROM => "DBI.pm", ABSTRACT_FROM => "DBI.pm", - MIN_PERL_VERSION => "5.008001", + MIN_PERL_VERSION => "5.012000", BUILD_REQUIRES => { "ExtUtils::MakeMaker" => "6.48", - "Test::Simple" => "0.90", + "Test::Simple" => "0.96", + }, + PREREQ_PM => { + "Module::Load" => 0.22, }, META_MERGE => { resources => { @@ -74,6 +77,7 @@ IRC => "irc://irc.perl.org/#dbi", }, suggests => { + "RPC::PlClient" => 0.2020, "RPC::PlServer" => 0.2020, "Net::Daemon" => 0, "SQL::Statement" => 1.414, @@ -89,8 +93,10 @@ OTHERLDFLAGS => "$::opt_g", }, clean => { - FILES => "\$(DISTVNAME) Perl.xsi t/zv*_*.t dbi__null_test_tmp* test_output_*" - ." dbiproxy$ext_pl dbiprof$ext_pl dbilogstrip$ext_pl dbiproxy.*log dbitrace.log dbi*.prof ndtest.prt" }, + FILES => join " " => "\$(DISTVNAME)", "Perl.xsi cover_db", + "t/zv*_*.t dbi__null_test_tmp* test_output_*", + "dbiproxy$ext_pl dbiprof$ext_pl dbilogstrip$ext_pl", + "dbiproxy.*log dbitrace.log dbi*.prof ndtest.prt" }, dist => { DIST_DEFAULT => "clean distcheck disttest tardist", PREOP => '$(MAKE) -f Makefile.old distdir', @@ -106,7 +112,7 @@ if ($gccversion =~ m/ clang ([0-9][-0-9.]*)/i) { print "Your perl was compiled with Clang (version $1). As this is not GCC, version checking is skipped.\n"; # https://clang.llvm.org/docs/DiagnosticsReference.html - $opts{DEFINE} .= " -W -Wall -Wpointer-arith -Wbad-function-cast"; + $opts{DEFINE} .= " -W -Wall -Wpointer-arith"; $opts{DEFINE} .= " -Wno-comment -Wno-sign-compare -Wno-cast-qual"; $opts{DEFINE} .= " -Wmissing-noreturn -Wno-unused-parameter"; $opts{DEFINE} .= " -Wno-compound-token-split-by-macro -Wno-constant-conversion"; @@ -120,7 +126,7 @@ if $gccversion =~ m/^\D*(1|2\.[1-8])\b/; print "Your perl was compiled with gcc (version $Config{gccversion}), okay.\n"; $gccversion =~ s/[^\d\.]//g; # just a number please - $opts{DEFINE} .= " -W -Wall -Wpointer-arith -Wbad-function-cast"; + $opts{DEFINE} .= " -W -Wall -Wpointer-arith"; $opts{DEFINE} .= " -Wno-comment -Wno-sign-compare -Wno-cast-qual"; $opts{DEFINE} .= " -Wmissing-noreturn -Wno-unused-parameter" if $gccversion ge "3.0"; if ($is_developer && $::opt_g) { @@ -152,9 +158,7 @@ # Set aside some values for post_initialize() in package MY my ($cfg_privlibexp, $cfg_archlibexp, $cfg_sitelibexp, $cfg_sitearchexp, $cfg_man3direxp) = @Config{qw( privlibexp archlibexp sitelibexp sitearchexp man3direxp )}; -for ($cfg_privlibexp, $cfg_archlibexp, $cfg_sitelibexp, $cfg_sitearchexp, $cfg_man3direxp) { - $_ = "" unless defined $_; - } +$_ //= "" for $cfg_privlibexp, $cfg_archlibexp, $cfg_sitelibexp, $cfg_sitearchexp, $cfg_man3direxp; my $conflictMsg = < 1 } + map { s{^(.*/)inline.h$}{-ignore_re $1.*}; $_ } + grep { -s $_ } + map { "$_/CORE/inline.h" } + @INC; + my $no_inc = join " " => "-ignore doc/make-doc.pl", sort keys %no_inc; my $min_vsn = ($] >= 5.024 && -d "xt" && -d "sandbox" && ($ENV{AUTOMATED_TESTING} || 0) != 1) ? join "\n" => + 'cover $make_sep test_cover:', + ' ccache -C', + ' -@rm -f *.gc??', + ' env AUTOMATED_TESTING=1 cover -test -jobs 17', + " cover -report html $no_inc", 'test ::', ' -@env TEST_FILES="xt/*.t" make -e test_dynamic', '' @@ -364,6 +380,7 @@ .PHONY: doc change changes spellcheck checkpod newdist changes: $(changes_pm1) doc: + perl dbixs_rev.pl perl doc/make-doc.pl --pod spellcheck checkpod: doc pod-spell-check -i -a doc/*.pod @@ -371,7 +388,7 @@ dbipport: perl sandbox/genPPPort_h.pl ppport: dbipport.h - perl dbipport.h --compat-version=5.8.1 DBI.xs + perl dbipport.h --compat-version=5.12.0 DBI.xs # make dist does not remove existing .tar.gz causing questions newdist: @@ -385,4 +402,10 @@ return $xst; } # post_constants +sub processPL { + my $super = shift->SUPER::processPL (@_); + $super =~ s/^(dbi\w+ :: dbi\w+\.PL pm_to_blib)/$1 \$(INST_DYNAMIC)/gm; + return $super; + } # processPL + 1; diff -Nru libdbi-perl-1.647/SECURITY.md libdbi-perl-1.652/SECURITY.md --- libdbi-perl-1.647/SECURITY.md 1970-01-01 00:00:00.000000000 +0000 +++ libdbi-perl-1.652/SECURITY.md 2026-08-11 14:04:51.000000000 +0000 @@ -0,0 +1,16 @@ +This is the Security Policy for Perl DBI. + +Report security issues via [GitHub Security Advisories](https://github.com/perl5-dbi/dbi/security/advisories). + +The maintainers will respond, and release fixes as soon as possible. +However, this project is maintained by volunteers, and they cannot guarantee a rapid response. + +If the issue is urgent, then you can also notify the CPAN Security Group (CPANSec) via email to . + +Please do not report security problems on public forums or in repository issues. + +Only the latest release of DBI will be supported. + +This policy was updated on 11 August 2026. + +If this policy or the release is more than two years old, then you should check for a more recent version of [DBI on CPAN](https://metacpan.org/dist/DBI) or the main branch of the [DBI git repository](https://github.com/perl5-dbi/dbi). diff -Nru libdbi-perl-1.647/cpanfile libdbi-perl-1.652/cpanfile --- libdbi-perl-1.647/cpanfile 2024-12-31 14:26:00.000000000 +0000 +++ libdbi-perl-1.652/cpanfile 2026-07-26 13:36:47.000000000 +0000 @@ -1,22 +1,24 @@ requires "XSLoader"; +requires "Module::Load" => "0.22"; -recommends "Encode" => "3.21"; +recommends "Encode" => "3.24"; -suggests "Clone" => "0.47"; +suggests "Clone" => "0.50"; suggests "DB_File"; suggests "MLDBM"; -suggests "Net::Daemon"; +suggests "Net::Daemon" => "0.52"; +suggests "RPC::PlClient" => "0.2020"; suggests "RPC::PlServer" => "0.2020"; suggests "SQL::Statement" => "1.414"; on "configure" => sub { - requires "ExtUtils::MakeMaker" => "6.48"; + requires "ExtUtils::MakeMaker" => "6.48"; - recommends "ExtUtils::MakeMaker" => "7.70"; + recommends "ExtUtils::MakeMaker" => "7.78"; }; on "test" => sub { - requires "Test::More" => "0.90"; + requires "Test::More" => "0.96"; - recommends "Test::More" => "1.302207"; + recommends "Test::More" => "1.302222"; }; diff -Nru libdbi-perl-1.647/dbipport.h libdbi-perl-1.652/dbipport.h --- libdbi-perl-1.647/dbipport.h 2024-12-31 14:37:02.000000000 +0000 +++ libdbi-perl-1.652/dbipport.h 2026-08-03 09:41:36.000000000 +0000 @@ -4,9 +4,9 @@ /* ---------------------------------------------------------------------- - dbipport.h -- Perl/Pollution/Portability Version 3.72 + dbipport.h -- Perl/Pollution/Portability Version 3.73 - Automatically created by Devel::PPPort running under perl 5.040000. + Automatically created by Devel::PPPort running under perl 5.042002. Do NOT edit this file directly! -- Edit PPPort_pm.PL and the includes in parts/inc/ instead. @@ -21,7 +21,7 @@ =head1 NAME -dbipport.h - Perl/Pollution/Portability version 3.72 +dbipport.h - Perl/Pollution/Portability version 3.73 =head1 SYNOPSIS @@ -302,6 +302,7 @@ sv_setpvf_mg() NEED_sv_setpvf_mg NEED_sv_setpvf_mg_GLOBAL sv_setpvf_mg_nocontext() NEED_sv_setpvf_mg_nocontext NEED_sv_setpvf_mg_nocontext_GLOBAL sv_unmagicext() NEED_sv_unmagicext NEED_sv_unmagicext_GLOBAL + sv_vstring_get() NEED_sv_vstring_get NEED_sv_vstring_get_GLOBAL utf8_to_uvchr_buf() NEED_utf8_to_uvchr_buf NEED_utf8_to_uvchr_buf_GLOBAL vload_module() NEED_vload_module NEED_vload_module_GLOBAL vmess() NEED_vmess NEED_vmess_GLOBAL @@ -588,7 +589,7 @@ # Disable broken TRIE-optimization BEGIN { eval '${^RE_TRIE_MAXBUF} = -1' if "$]" >= "5.009004" && "$]" <= "5.009005"} -my $VERSION = 3.72; +my $VERSION = 3.73; my %opt = ( quiet => 0, @@ -7089,6 +7090,7 @@ Perl_sqrt|5.006000|5.006000|n PERL_STACK_OFFSET_DEFINED|||piu PERL_STACK_OVERFLOW_CHECK|5.006000||Viu +PERL_STACK_REALIGN|||piu PERL_STATIC_FORCE_INLINE|5.031011||Viu PERL_STATIC_FORCE_INLINE_NO_RET|5.031011||Viu PERL_STATIC_INLINE|5.013004|5.013004|poVn @@ -9445,7 +9447,7 @@ sv_ref|5.023005|5.023005| SvREFCNT|5.003007|5.003007| SvREFCNT_dec|5.003007|5.003007| -SvREFCNT_dec_NN|5.017007|5.017007| +SvREFCNT_dec_NN|5.017007|5.017007|p SvREFCNT_IMMORTAL|5.017008||Viu SvREFCNT_inc|5.003007|5.003007|pn SvREFCNT_inc_NN|5.009004|5.003007|pn @@ -9631,11 +9633,13 @@ sv_vcatpvf_mg|5.006000|5.004000|p sv_vcatpvfn|5.004000|5.004000| sv_vcatpvfn_flags|5.017002|5.017002| -SvVOK|5.008001|5.008001| +SvVOK|5.008001|5.008001|p sv_vsetpvf|5.006000|5.004000|p sv_vsetpvf_mg|5.006000|5.004000|p sv_vsetpvfn|5.004000|5.004000| +sv_vstring_get|||p SvVSTRING_mg|5.009004|5.003007|p +SvVSTRING|||piu SvWEAKREF|5.006000||Viu SvWEAKREF_off|5.006000||Viu SvWEAKREF_on|5.006000||Viu @@ -12513,6 +12517,19 @@ #endif #endif + +/* work around a stack alignment bug in 32-bit GCC on Windows */ +#if defined(WIN32) && !defined(WIN64) && defined(__GNUC__) +#ifndef PERL_STACK_REALIGN +# define PERL_STACK_REALIGN __attribute__((force_align_arg_pointer)) +#endif + +#else +#ifndef PERL_STACK_REALIGN +# define PERL_STACK_REALIGN +#endif + +#endif #ifndef cBOOL # define cBOOL(cbool) ((cbool) ? (bool)1 : (bool)0) #endif @@ -15932,6 +15949,11 @@ (void)((PL_Sv=(SV*)(sv)) ? ++(SvREFCNT(PL_Sv)) : 0) # endif #endif + +/* not as efficient as the real thing, but it works */ +#ifndef SvREFCNT_dec_NN +# define SvREFCNT_dec_NN(sv) SvREFCNT_dec(sv) +#endif #ifndef SvREFCNT_inc_simple_void # define SvREFCNT_inc_simple_void(sv) STMT_START { if (sv) SvREFCNT(sv)++; } STMT_END #endif @@ -16595,6 +16617,49 @@ #endif #endif +#ifndef SvVSTRING +# define SvVSTRING(sv, len) (sv_vstring_get(sv, &(len))) +#endif + +#ifndef SvVOK +# define SvVOK(sv) (FALSE) +#endif + +#if !defined(sv_vstring_get) + +#if defined(NEED_sv_vstring_get) +static const char * DPPP_(my_sv_vstring_get)(pTHX_ SV * sv, STRLEN * lenp); +static +#else +extern const char * DPPP_(my_sv_vstring_get)(pTHX_ SV * sv, STRLEN * lenp); +#endif + +#if defined(NEED_sv_vstring_get) || defined(NEED_sv_vstring_get_GLOBAL) + +#ifdef sv_vstring_get +# undef sv_vstring_get +#endif +#define sv_vstring_get(a,b) DPPP_(my_sv_vstring_get)(aTHX_ a,b) +#define Perl_sv_vstring_get DPPP_(my_sv_vstring_get) + + +const char * +DPPP_(my_sv_vstring_get)(pTHX_ SV *sv, STRLEN *lenp) +{ +#ifdef SvVSTRING_mg + MAGIC *mg = SvVSTRING_mg(sv); + if (!mg) return NULL; + + if (lenp) *lenp = mg->mg_len; + return mg->mg_ptr; +#else + return NULL; +#endif +} + +#endif + +#endif #ifdef USE_ITHREADS #ifndef CopFILE diff -Nru libdbi-perl-1.647/dbixs_rev.h libdbi-perl-1.652/dbixs_rev.h --- libdbi-perl-1.647/dbixs_rev.h 2025-01-17 14:09:59.000000000 +0000 +++ libdbi-perl-1.652/dbixs_rev.h 2026-08-15 11:28:22.000000000 +0000 @@ -1,2 +1,4 @@ -/* Fri Jan 17 15:09:59 2025 */ -#define DBIXS_REVISION 1698 +/* Sat Aug 15 13:28:22 2026 */ +#define DBIXS_RELEASE 1 +#define DBIXS_VERSION 652 +#define DBIXS_REVISION 1810 diff -Nru libdbi-perl-1.647/dbixs_rev.pl libdbi-perl-1.652/dbixs_rev.pl --- libdbi-perl-1.647/dbixs_rev.pl 2025-01-17 13:55:51.000000000 +0000 +++ libdbi-perl-1.652/dbixs_rev.pl 2025-03-14 14:13:20.000000000 +0000 @@ -14,10 +14,20 @@ -d ".git" or skip_update ("No git env"); +my ($dbir, $dbiv); +open my $fh, "<", "DBI.pm" or die "DBI.pm: $!\n"; +while (<$fh>) { + m/\b VERSION \s*=\s* (["']) ([0-9]+) \. ([0-9]+) \1/x or next; + ($dbir, $dbiv) = ($2, $3); + close $fh; + last; + } +$dbir or die "Cannot fetch DBI version from DBI.pm\n"; + my @n = eval { qx{git log --pretty=oneline} }; @n or skip_update ("Git log was empty"); -open my $fh, ">$dbixs_rev_file" or die "Can't open $dbixs_rev_file: $!\n"; +open $fh, ">$dbixs_rev_file" or die "Can't open $dbixs_rev_file: $!\n"; print $fh "/* ", scalar localtime, " */\n"; chomp (my @st = qx{git status -s --show-stash}); @@ -25,6 +35,8 @@ my $def = "DBIXS_REVISION"; my $rev = scalar @n; +print $fh "#define DBIXS_RELEASE $dbir\n"; +print $fh "#define DBIXS_VERSION $dbiv\n"; print $fh "#define $def $rev\n"; close $fh or die "Error closing $dbixs_rev_file: $!\n"; -print "Wrote $def $rev to $dbixs_rev_file\n"; +print "Wrote $def $rev to $dbixs_rev_file for DBI-$dbir.$dbiv\n"; diff -Nru libdbi-perl-1.647/debian/changelog libdbi-perl-1.652/debian/changelog --- libdbi-perl-1.647/debian/changelog 2026-06-09 14:04:25.000000000 +0000 +++ libdbi-perl-1.652/debian/changelog 2026-08-22 20:43:46.000000000 +0000 @@ -1,11 +1,78 @@ -libdbi-perl (1.647-1+deb13u1) trixie-security; urgency=high +libdbi-perl (1.652-2~deb13u1) trixie-security; urgency=high * Team upload. - * Fix possible stack overflow (CVE-2026-9698) - * Replacing `?` with `:p#` in `preparse ()` with more than 9 `?` causes - buffer overflow (CVE-2026-10879) + * Rebuild for trixie-security + * Revert "Remove «Priority: optional», which is the current default." + * Revert "Remove «Rules-Requires-Root: no», which is the current default." + * Revert "Declare compliance with Debian Policy 4.7.4." + * Revert "Reformat debian/control." - -- Salvatore Bonaccorso Tue, 09 Jun 2026 16:04:25 +0200 + -- Salvatore Bonaccorso Sat, 22 Aug 2026 22:43:46 +0200 + +libdbi-perl (1.652-2) unstable; urgency=medium + + * Add patch from upstream Git to fix 32bit test failure. + Thanks to Adrian Bunk for the bug report. (Closes: #1144851) + + -- gregor herrmann Wed, 19 Aug 2026 17:14:08 +0200 + +libdbi-perl (1.652-1) unstable; urgency=medium + + * Import upstream version 1.652. + + Limit statements to 292 Mb in preparse (CVE-2026-73193) + (Closes: #1144470) + + Force placeholder limit on :# and :p# too (CVE-2026-73194) + (Closes: #1144471) + * Install new SECURITY.md file. + * Refresh t__40profile.t__NTP.patch (offset). + + -- gregor herrmann Sat, 15 Aug 2026 17:13:20 +0200 + +libdbi-perl (1.651-1) unstable; urgency=medium + + * Import upstream version 1.651. + - Fix inverted comparisons for strings in DBI::SQL::Nano + (CVE-2026-15043) + - Fix DBD::File to ensure that the table is not a symlink outside of f_dir + (CVE-2026-15392) + - Fix an out-of-bounds error when a statement handle has no fields but the + source row is not empty (CVE-2026-60082) + - Add an overridable upper bound $MAX_PATH_DEPTH for DBI::ProfileData + (CVE-2026-60081) + Closes: #1142072 + + -- gregor herrmann Wed, 15 Jul 2026 17:29:24 +0200 + +libdbi-perl (1.650-1) unstable; urgency=medium + + * Import upstream version 1.650. + - Set a hard limit of 99999 on '?' placeholders + (CVE-2026-14739) + - Fix out-of-bounds read in preparse of SQL that starts with a comment + (CVE-2026-14740) + - Fix code injection via Profile DSN attribute or DBI_PROFILE variable + (CVE-2026-14380) + Closes: #1141667 + * Install new upstream document. + + -- gregor herrmann Wed, 08 Jul 2026 18:31:30 +0200 + +libdbi-perl (1.649-1) unstable; urgency=medium + + * Import upstream version 1.649. + + -- gregor herrmann Sun, 05 Jul 2026 20:48:53 +0200 + +libdbi-perl (1.648-1) unstable; urgency=medium + + * Import upstream version 1.648. + Fixes CVE-2026-9698 and CVE-2026-10879. + * Update years of upstream and packaging copyright. + * Declare compliance with Debian Policy 4.7.4. + * Remove «Rules-Requires-Root: no», which is the current default. + * Remove «Priority: optional», which is the current default. + + -- gregor herrmann Sat, 06 Jun 2026 01:45:08 +0200 libdbi-perl (1.647-1) unstable; urgency=medium diff -Nru libdbi-perl-1.647/debian/copyright libdbi-perl-1.652/debian/copyright --- libdbi-perl-1.647/debian/copyright 2026-06-09 14:04:25.000000000 +0000 +++ libdbi-perl-1.652/debian/copyright 2026-08-22 20:43:46.000000000 +0000 @@ -4,8 +4,8 @@ Source: https://metacpan.org/release/DBI Files: * -Copyright: 1994-2025, Tim Bunce - 2024-2025, DBI Team +Copyright: 1994-2026, Tim Bunce + 2024-2026, DBI Team License: Artistic or GPL-1+ Files: Driver.xst @@ -114,7 +114,7 @@ 1999-2007, Christian Hammers 2002, Dirk Eddelbuettel 2007-2017, Damyan Ivanov - 2008-2025, gregor herrmann + 2008-2026, gregor herrmann 2008, Niko Tyni 2009, Ryan Niebur 2010, Ansgar Burchardt diff -Nru libdbi-perl-1.647/debian/libdbi-perl.docs libdbi-perl-1.652/debian/libdbi-perl.docs --- libdbi-perl-1.647/debian/libdbi-perl.docs 2026-06-09 14:04:25.000000000 +0000 +++ libdbi-perl-1.652/debian/libdbi-perl.docs 2026-08-22 20:43:46.000000000 +0000 @@ -1 +1,3 @@ debian/NEWS.Developer +CONTRIBUTING.md +SECURITY.md diff -Nru libdbi-perl-1.647/debian/patches/0001-Fix-test-for-32bit-perl-report-in-PR-189.patch libdbi-perl-1.652/debian/patches/0001-Fix-test-for-32bit-perl-report-in-PR-189.patch --- libdbi-perl-1.647/debian/patches/0001-Fix-test-for-32bit-perl-report-in-PR-189.patch 1970-01-01 00:00:00.000000000 +0000 +++ libdbi-perl-1.652/debian/patches/0001-Fix-test-for-32bit-perl-report-in-PR-189.patch 2026-08-22 20:43:46.000000000 +0000 @@ -0,0 +1,37 @@ +From acc903db3086a737936643a5dbbc0e22f9d5c421 Mon Sep 17 00:00:00 2001 +From: "H.Merijn Brand - Tux" +Date: Mon, 17 Aug 2026 09:49:56 +0200 +Subject: [PATCH] Fix test for 32bit-perl (report in PR#189) + +Origin: https://github.com/perl5-dbi/dbi/commit/acc903db3086a737936643a5dbbc0e22f9d5c421 +Bug-Debian: https://bugs.debian.org/1144851 +Bug: https://github.com/perl5-dbi/dbi/pull/189 + +--- + ChangeLog | 3 +++ + DBI.pm | 4 ++-- + dbixs_rev.h | 4 ++-- + doc/DBI.3 | 4 ++-- + doc/DBI.html | 2 +- + doc/DBI.man | 4 ++-- + doc/DBI.md | 2 +- + lib/DBI/Changes.pm | 10 ++++++++++ + t/60preparse.t | 2 +- + 9 files changed, 24 insertions(+), 11 deletions(-) + +diff --git a/t/60preparse.t b/t/60preparse.t +index 92bdc89..eff39be 100755 +--- a/t/60preparse.t ++++ b/t/60preparse.t +@@ -148,7 +148,7 @@ ok( $DBI::err ); + is( $DBI::errstr, "preparse found :p100000 which is outside the allowed range."); + is( pp($dbh, 'a = :2147483648', DBIpp_ph_qm, DBIpp_ph_cs|DBIpp_ph_cn), undef, 'exceeds limit'); + ok( $DBI::err ); +-is( $DBI::errstr, "preparse found :p-2147483648 which is outside the allowed range."); ++like( $DBI::errstr, qr{^preparse found :p(?:-2147483648|2147483647) which is outside the allowed range.$}); + is( pp($dbh, 'a = :12345678987654321', DBIpp_ph_qm, DBIpp_ph_cs|DBIpp_ph_cn), undef, 'exceeds limit'); + ok( $DBI::err ); + like( $DBI::errstr, qr{^preparse found :p\d+ which is outside the allowed range.$}); +-- +2.55.0 + diff -Nru libdbi-perl-1.647/debian/patches/series libdbi-perl-1.652/debian/patches/series --- libdbi-perl-1.647/debian/patches/series 2026-06-09 14:04:25.000000000 +0000 +++ libdbi-perl-1.652/debian/patches/series 2026-08-22 20:43:46.000000000 +0000 @@ -1,5 +1,4 @@ t__06attrs.t__localefix.patch t__40profile.t__NTP.patch t__80proxy.t___syslogd.patch -Fix-possible-stack-overflow-old-issue-already-noted-.patch -Replacing-with-p-in-preparse-with-more-than-9-causes.patch +0001-Fix-test-for-32bit-perl-report-in-PR-189.patch diff -Nru libdbi-perl-1.647/debian/patches/t__40profile.t__NTP.patch libdbi-perl-1.652/debian/patches/t__40profile.t__NTP.patch --- libdbi-perl-1.647/debian/patches/t__40profile.t__NTP.patch 2026-06-09 14:04:25.000000000 +0000 +++ libdbi-perl-1.652/debian/patches/t__40profile.t__NTP.patch 2026-08-22 20:43:46.000000000 +0000 @@ -1,11 +1,12 @@ Description: A NTP clock adjustment of -0.00967s lead to FTBFS. Reviewed-by: Nicholas Bamber -Last-Update: 2010-10-07 +Last-Update: 2026-08-15 Forwarded: not-needed Author: gregor hermann + --- a/t/40profile.t +++ b/t/40profile.t -@@ -177,7 +177,7 @@ +@@ -171,7 +171,7 @@ this kind of clock behaviour. Please retry. EOT # don't treat small negative values as failure diff -Nru libdbi-perl-1.647/ex/unicode_test.pl libdbi-perl-1.652/ex/unicode_test.pl --- libdbi-perl-1.647/ex/unicode_test.pl 2024-08-13 12:11:45.000000000 +0000 +++ libdbi-perl-1.652/ex/unicode_test.pl 2026-07-14 10:01:43.000000000 +0000 @@ -229,6 +229,7 @@ # DBD::CSV seems to get upset by the mixed_lob_unicode_data test # and fails to drop the table with: # Execution ERROR: utf8 "\x89" does not map to Unicode at /usr/lib/perl/5.10/IO/Handle.pm line 167. + # TODO: Check if this is still the case for perl-5.12 and up unlink 'fred.csv' if $driver eq 'CSV'; #diag($@) if $@; } diff -Nru libdbi-perl-1.647/lib/DBD/DBM.pm libdbi-perl-1.652/lib/DBD/DBM.pm --- libdbi-perl-1.647/lib/DBD/DBM.pm 2024-08-19 07:15:38.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/DBM.pm 2026-08-15 08:50:43.000000000 +0000 @@ -16,7 +16,7 @@ # DBD AUTHORS - see the comments in the code # ####################################################################### -require 5.008; +require 5.012; use strict; use warnings; @@ -138,6 +138,7 @@ dbm_readonly_attrs => 1, # DBD::DBM::db r/o attrs dbm_meta => 1, # DBD::DBM public access for f_meta dbm_tables => 1, # DBD::DBM public access for f_meta + dbm_updatable_key => 1, }; $dbh->{dbm_readonly_attrs} = { dbm_version => 1, # verbose DBD::DBM version @@ -280,8 +281,7 @@ $meta->{dbm_mldbm} ||= $dbh->{dbm_mldbm} if ( $dbh->{dbm_mldbm} ); $meta->{dbm_berkeley_flags} ||= $dbh->{dbm_berkeley_flags}; - defined $meta->{f_ext} - or $meta->{f_ext} = $dbh->{f_ext}; + $meta->{f_ext} //= $dbh->{f_ext}; unless ( defined( $meta->{f_ext} ) ) { my $ext; @@ -343,6 +343,11 @@ defined( $dbh->{dbm_cols} ) and $meta->{col_names} = $dbh->{dbm_cols}; } + unless ( defined( $meta->{dbm_updatable_key} ) ) + { + defined( $dbh->{dbm_updatable_key} ) and $meta->{dbm_updatable_key} = $dbh->{dbm_updatable_key}; + } + $self->SUPER::init_table_meta( $dbh, $meta, $table ); } @@ -569,6 +574,14 @@ my $key = shift @$origary; my $newkey = shift @$aryref; return unless ( defined $key ); + + if ( my $mode = $meta->{dbm_updatable_key} ) { + my $method = $mode == 1 ? \&carp : \&croak; + my $exists; + eval { $exists = $key ne $newkey && exists( $meta->{hash}->{$newkey} ); }; + $exists and $method->("Row with PK '$newkey' already exists"); + } + $key eq $newkey or delete $meta->{hash}->{$key}; my $row = ( ref($aryref) eq 'ARRAY' ) ? $aryref : [$aryref]; $meta->{hash}->{$newkey} = $meta->{dbm_mldbm} ? $row : $row->[0]; @@ -1022,6 +1035,26 @@ evaluation of SQL::Statement might beat DBI::SQL::Nano every time the where clause contains not only the key field (or more than one). +=head2 Updatable Keys + +The default behavior is that updating a key to the same value of an +existing key will succeed, and the existing row will be overwritten. + +There may be cases where this is undesirable. + +This behavior can now be configured, by setting C +attribute. To emit a warning when this happens, set it to C<1>: + + $dbh = DBI->connect('dbi:DBM:', undef, undef, { + dbm_updatable_key => 1, + } ); + +To instead die when that happens, set it to C<2>: + + $dbh = DBI->connect('dbi:DBM:', undef, undef, { + dbm_updatable_key => 2, + } ); + =head2 Supported SQL syntax DBD::DBM uses a subset of SQL. The robustness of that subset depends on diff -Nru libdbi-perl-1.647/lib/DBD/File/Developers.pod libdbi-perl-1.652/lib/DBD/File/Developers.pod --- libdbi-perl-1.647/lib/DBD/File/Developers.pod 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/File/Developers.pod 2026-07-13 08:44:58.000000000 +0000 @@ -8,8 +8,7 @@ use base qw( DBD::File ); - sub driver - { + sub driver { ... my $drh = $proto->SUPER::driver ($attr); ... @@ -108,8 +107,7 @@ package DBD::DBM::dr; @DBD::DBM::dr::ISA = qw( DBD::File::dr ); - sub connect ($$;$$$) - { + sub connect ($$;$$$) { ... } @@ -152,8 +150,7 @@ use base qw( DBD::File ); - sub driver - { + sub driver { my ($class, $attr) = @_; ... my $drh = $class->SUPER::driver ($attr); @@ -363,14 +360,12 @@ package DBD::File::TableSource::FileSystem; - sub data_sources ($;$) - { + sub data_sources ($;$) { my ($class, $drh, $attrs) = @_; ... } - sub avail_tables - { + sub avail_tables { my ($class, $drh) = @_; ... } @@ -404,8 +399,7 @@ @DBD::File::DataSource::Stream::ISA = 'DBI::DBD::SqlEngine::DataSource'; - sub complete_table_name - { + sub complete_table_name { my ($self, $meta, $file, $respect_case) = @_; ... } @@ -416,8 +410,7 @@ package DBD::File::DataSource::Stream; - sub apply_encoding - { + sub apply_encoding { my ($self, $meta, $fn) = @_; ... } @@ -427,8 +420,7 @@ package DBD::File::DataSource::Stream; - sub open_data - { + sub open_data { my ($self, $meta, $attrs, $flags) = @_; ... } @@ -446,8 +438,7 @@ package DBD::File::DataSource::File; - sub complete_table_name ($$;$) - { + sub complete_table_name ($$;$) { my ($self, $meta, $table, $respect_case) = @_; ... } @@ -464,8 +455,7 @@ package DBD::File::DataSource::File; - sub open_data ($) - { + sub open_data ($) { my ($self, $meta, $attrs, $flags) = @_; ... } @@ -493,15 +483,14 @@ to add anything to the following implementation, an empty DBD::XXX::Statement package satisfies DBD::File. - sub open_table ($$$$$) - { + sub open_table ($$$$$) { my ($self, $data, $table, $createMode, $lockMode) = @_; my $class = ref $self; $class =~ s/::Statement/::Table/; my $flags = { - createMode => $createMode, + createMode => $createMode, lockMode => $lockMode, }; $self->{command} eq "DROP" and $flags->{dropMode} = 1; @@ -647,14 +636,14 @@ The module DBD::File is currently maintained by -H.Merijn Brand < h.m.brand at xs4all.nl > and +H.Merijn Brand < hmbrand at cpan.org > and Jens Rehsack < rehsack at googlemail.com > The original author is Jochen Wiedmann. =head1 COPYRIGHT AND LICENSE -Copyright (C) 2010-2013 by H.Merijn Brand & Jens Rehsack +Copyright (C) 2010-2026 by H.Merijn Brand & Jens Rehsack All rights reserved. diff -Nru libdbi-perl-1.647/lib/DBD/File/HowTo.pod libdbi-perl-1.652/lib/DBD/File/HowTo.pod --- libdbi-perl-1.647/lib/DBD/File/HowTo.pod 2024-08-19 07:15:38.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/File/HowTo.pod 2026-05-29 09:45:56.000000000 +0000 @@ -92,36 +92,33 @@ package DBD::Foo::Table; - sub bootstrap_table_meta - { - my ( $self, $dbh, $meta, $table ) = @_; + sub bootstrap_table_meta { + my ($self, $dbh, $meta, $table) = @_; # initialize all $meta attributes which might be relevant for # file2table - return $self->SUPER::bootstrap_table_meta($dbh, $meta, $table); - } + return $self->SUPER::bootstrap_table_meta ($dbh, $meta, $table); + } - sub init_table_meta - { - my ( $self, $dbh, $meta, $table ) = @_; + sub init_table_meta { + my ($self, $dbh, $meta, $table) = @_; # called after $meta contains the results from file2table # initialize all missing $meta attributes - $self->SUPER::init_table_meta( $dbh, $meta, $table ); - } + $self->SUPER::init_table_meta ($dbh, $meta, $table); + } In case C doesn't open the files as the driver needs that, override it! - sub open_file - { - my ( $self, $meta, $attrs, $flags ) = @_; + sub open_file { + my ($self, $meta, $attrs, $flags) = @_; # ensure that $meta->{f_dontopen} is set - $self->SUPER::open_file( $meta, $attrs, $flags ); + $self->SUPER::open_file ($meta, $attrs, $flags); # now do what ever needs to be done - } + } Combined with the methods implemented using the L guide, the table is full working and you could try a start over. @@ -135,8 +132,7 @@ C<< $dbh->{$drv_prefix . "_meta"} >> attribute which cares, so what you learned at this place before, is still valid. - sub init_valid_attributes - { + sub init_valid_attributes { my $dbh = $_[0]; $dbh->SUPER::init_valid_attributes (); @@ -147,7 +143,7 @@ $dbh->{foo_meta} = "foo_tables"; return $dbh; - } + } See updates at L. @@ -164,12 +160,12 @@ The module DBD::File is currently maintained by -H.Merijn Brand < h.m.brand at xs4all.nl > and +H.Merijn Brand < hmbrand at cpan.org > and Jens Rehsack < rehsack at googlemail.com > =head1 COPYRIGHT AND LICENSE -Copyright (C) 2010 by H.Merijn Brand & Jens Rehsack +Copyright (C) 2010-2026 by H.Merijn Brand & Jens Rehsack All rights reserved. diff -Nru libdbi-perl-1.647/lib/DBD/File/Roadmap.pod libdbi-perl-1.652/lib/DBD/File/Roadmap.pod --- libdbi-perl-1.647/lib/DBD/File/Roadmap.pod 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/File/Roadmap.pod 2026-07-04 14:26:36.000000000 +0000 @@ -152,7 +152,7 @@ =head1 RESOURCES AND CONTRIBUTIONS -See L for I. +See F for I. If your company has benefited from DBI, please consider if it could make a donation to The Perl Foundation "DBI Development" diff -Nru libdbi-perl-1.647/lib/DBD/File.pm libdbi-perl-1.652/lib/DBD/File.pm --- libdbi-perl-1.647/lib/DBD/File.pm 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/File.pm 2026-07-14 11:20:05.000000000 +0000 @@ -10,7 +10,7 @@ # # The original author is Jochen Wiedmann. # -# Copyright (C) 2009-2020 by H.Merijn Brand & Jens Rehsack +# Copyright (C) 2009-2026 by H.Merijn Brand & Jens Rehsack # Copyright (C) 2004 by Jeff Zucker # Copyright (C) 1998 by Jochen Wiedmann # @@ -20,7 +20,7 @@ # General Public License or the Artistic License, as specified in # the Perl README file. -require 5.008; +require 5.012; use strict; use warnings; @@ -35,7 +35,7 @@ use base qw( DBI::DBD::SqlEngine ); use Carp; -our $VERSION = "0.44"; +our $VERSION = "0.45"; our $drh = undef; # holds driver handle(s) once initialized @@ -457,7 +457,7 @@ while (defined ($file = $dirh->read ())) { my ($tbl, $meta) = $class->get_table_meta ($dbh, $file, 0, 0) or next; # XXX # $tbl && $meta && -f $meta->{f_fqfn} or next; - $seen{defined $schema ? $schema : "\0"}{$dir}{$tbl}++ or + $seen{$schema // "\0"}{$dir}{$tbl}++ or push @tables, [ undef, $schema, $tbl, "TABLE", "FILE" ]; } $dirh->close () or @@ -511,7 +511,7 @@ sub apply_encoding { my ($self, $meta, $fn) = @_; - defined $fn or $fn = "file handle " . fileno ($meta->{fh}); + $fn //= "file handle " . fileno ($meta->{fh}); if (my $enc = $meta->{f_encoding}) { binmode $meta->{fh}, ":encoding($enc)" or croak "Failed to set encoding layer '$enc' on $fn: $!"; @@ -565,6 +565,7 @@ our @ISA = "DBD::File::DataSource::Stream"; use Carp; +require List::Util; my $fn_any_ext_regex = qr/\.[^.]*/; @@ -624,6 +625,19 @@ -d $searchdir or croak "-d $searchdir: $!"; + # If the file location is outside the current folder, + # its absolute path should be in ($f_dir, @f_dir_search) + # Note this triggers only when *used*, not at definition time + # $dbh->{csv_tables}{foo}{file} = "/out/side/scope/foo.csv"; # OK + # $dbh->do ("create table foo (c char)"); # FAIL + my @bases = map { Cwd::abs_path ($_) } $meta->{f_dir}, @{$meta->{f_dir_search} || []}; + if ($searchdir) { + my $sd = Cwd::abs_path ($searchdir); + unless (List::Util::first { $_ eq $sd } @bases) { + croak "Using data files in $searchdir is unsafe and not allowed.\nUse f_dir or f_dir_search.\n"; + } + } + $searchdir eq $meta->{f_dir} and $dir = ""; @@ -680,6 +694,14 @@ $meta->{f_fqln} = $meta->{f_fqbn} . $meta->{f_lockfile}; $dir && !$user_spec_file and $tbl = File::Spec->catfile ($dir, $tbl); + + if (-l $fqfn) { + my $real = Cwd::abs_path ($fqfn); + unless (List::Util::any { $real =~ m{^\Q$_\E} } @bases) { + croak "Data file $fqfn is a outside of f_dir f_and f_dir_search\n"; + } + } + $meta->{table_name} = $tbl; return $tbl; @@ -910,7 +932,7 @@ my ($self, $data, $pos, $whence) = @_; my $meta = $self->{meta}; if ($whence == 0 && $pos == 0) { - $pos = defined $meta->{first_row_pos} ? $meta->{first_row_pos} : 0; + $pos = $meta->{first_row_pos} // 0; } elsif ($whence != 2 || $pos != 0) { croak "Illegal seek position: pos = $pos, whence = $whence"; @@ -1286,8 +1308,7 @@ Signature: - sub f_versions (;$) - { + sub f_versions (;$) { my ($table_name) = @_; $table_name ||= "."; ... @@ -1411,14 +1432,14 @@ This module is currently maintained by -H.Merijn Brand < h.m.brand at xs4all.nl > and +H.Merijn Brand < hmbrand at cpan.org > and Jens Rehsack < rehsack at googlemail.com > The original author is Jochen Wiedmann. =head1 COPYRIGHT AND LICENSE - Copyright (C) 2009-2020 by H.Merijn Brand & Jens Rehsack + Copyright (C) 2009-2026 by H.Merijn Brand & Jens Rehsack Copyright (C) 2004-2009 by Jeff Zucker Copyright (C) 1998-2004 by Jochen Wiedmann diff -Nru libdbi-perl-1.647/lib/DBD/Gofer/Transport/Base.pm libdbi-perl-1.652/lib/DBD/Gofer/Transport/Base.pm --- libdbi-perl-1.647/lib/DBD/Gofer/Transport/Base.pm 2025-01-17 09:56:00.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/Gofer/Transport/Base.pm 2026-07-14 11:12:50.000000000 +0000 @@ -177,7 +177,7 @@ if (my $go_retry_hook = $self->go_retry_hook) { my $retry = $go_retry_hook->($request, $response, $self); $self->trace_msg(sprintf "go_retry_hook returned %s\n", - (defined $retry) ? $retry : 'undef'); + $retry // 'undef'); return $retry if defined $retry; } diff -Nru libdbi-perl-1.647/lib/DBD/Gofer/Transport/stream.pm libdbi-perl-1.652/lib/DBD/Gofer/Transport/stream.pm --- libdbi-perl-1.647/lib/DBD/Gofer/Transport/stream.pm 2013-06-03 17:15:23.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/Gofer/Transport/stream.pm 2026-07-14 11:12:16.000000000 +0000 @@ -34,7 +34,7 @@ my ($self) = @_; my $persist = $self->go_persist; # = 0 can force non-caching - $persist = $persist_all if not defined $persist; + $persist //= $persist_all; my $key = ($persist) ? $self->_connection_key : ''; if ($persist{$key} && $self->_connection_check($persist{$key})) { $self->trace_msg("reusing persistent connection $key\n",0) if $self->trace >= 1; diff -Nru libdbi-perl-1.647/lib/DBD/Mem.pm libdbi-perl-1.652/lib/DBD/Mem.pm --- libdbi-perl-1.647/lib/DBD/Mem.pm 2025-01-17 09:46:48.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/Mem.pm 2026-07-14 11:17:10.000000000 +0000 @@ -15,7 +15,7 @@ # General Public License or the Artistic License, as specified in # the Perl README file. -require 5.008; +require 5.012; use strict; ################# @@ -175,7 +175,7 @@ sub open_data ($) { my ( $self, $meta, $attrs, $flags ) = @_; - defined $meta->{data_tbl} or $meta->{data_tbl} = []; + $meta->{data_tbl} //= []; } ######################## @@ -207,7 +207,7 @@ { my ( $self, $dbh, $meta, $table ) = @_; - defined $meta->{sql_data_source} or $meta->{sql_data_source} = "DBD::Mem::DataSource"; + $meta->{sql_data_source} //= "DBD::Mem::DataSource"; $meta; } diff -Nru libdbi-perl-1.647/lib/DBD/Proxy.pm libdbi-perl-1.652/lib/DBD/Proxy.pm --- libdbi-perl-1.647/lib/DBD/Proxy.pm 2025-01-17 09:46:48.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/Proxy.pm 2026-07-14 11:13:36.000000000 +0000 @@ -449,7 +449,7 @@ 'TYPE' => $types, 'cache_filled' => 1 }, - 'proxy_cache_only' => 1, + 'proxy_cache_only' => 1, }); $sth->SUPER::STORE('NUM_OF_FIELDS' => $numFields); $inner->{NAME} = $names; @@ -592,7 +592,7 @@ my $data = $sth->{'proxy_data'}; - $sth->{'proxy_rows'} = 0 unless defined $sth->{'proxy_rows'}; + $sth->{'proxy_rows'} //= 0; if(!$data || !@$data) { return undef unless $sth->SUPER::FETCH('Active'); @@ -624,7 +624,7 @@ sub rows ($) { my $rows = shift->{'proxy_rows'}; - return (defined $rows) ? $rows : -1; + return ($rows // -1); } sub finish ($) { @@ -634,7 +634,7 @@ $sth->SUPER::STORE('Active' => 0); return 0 unless $rsth; # Something's out of sync my $no_finish = exists($sth->{'proxy_no_finish'}) - ? $sth->{'proxy_no_finish'} + ? $sth->{'proxy_no_finish'} : $sth->FETCH('Database')->{'proxy_no_finish'}; unless ($no_finish) { local $SIG{__DIE__} = 'DEFAULT'; diff -Nru libdbi-perl-1.647/lib/DBD/Sponge.pm libdbi-perl-1.652/lib/DBD/Sponge.pm --- libdbi-perl-1.647/lib/DBD/Sponge.pm 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/lib/DBD/Sponge.pm 2026-05-28 11:36:38.000000000 +0000 @@ -93,12 +93,15 @@ || [ map { "col$_" } 1..$numFields ]; $sth->{TYPE} = $attribs->{TYPE} || [ (DBI::SQL_VARCHAR()) x $numFields ]; - $sth->{PRECISION} = $attribs->{PRECISION} - || [ map { length($sth->{NAME}->[$_]) } 0..$numFields -1 ]; $sth->{SCALE} = $attribs->{SCALE} || [ (0) x $numFields ]; $sth->{NULLABLE} = $attribs->{NULLABLE} || [ (2) x $numFields ]; + # Allow user to specify precision, otherwise + # FETCH will lazily compute if needed + if ($attribs->{PRECISION}) { + $sth->{PRECISION} = $attribs->{PRECISION}; + } } $outer; @@ -154,6 +157,7 @@ return $dbh->set_err(42, "not enough parameters") unless @args >= 2; return \@args; } + } @@ -202,6 +206,11 @@ my ($sth, $attrib) = @_; # would normally validate and only fetch known attributes # else pass up to DBI to handle + + if ($attrib eq 'PRECISION') { + # prepare() did _not_ specify PRECISION, so lazily compute it now + return $sth->{PRECISION} = _max_col_lengths(@{$sth}{'NUM_OF_FIELDS', 'rows'}); + } return $sth->SUPER::FETCH($attrib); } @@ -211,6 +220,22 @@ # else pass up to DBI to handle return $sth->SUPER::STORE($attrib, $value); } + + sub _max_col_lengths { + # compute our columns' PRECISION (data length) by looking for the + # max lengths of each column's data, row by row + my ($num_of_fields, $rows) = @_; + my @precision = (0,) x $num_of_fields; + my $n = $num_of_fields - 1; + my $len; + for my $row (@$rows) { + for my $i (0 .. $n) { + next unless defined($len = length($row->[$i])); + $precision[$i] = $len if $len > $precision[$i]; + } + } + return \@precision; + } } 1; @@ -261,33 +286,38 @@ =item * -The C<$statement> here is an arbitrary statement or name you want -to provide as identity of your data. If you're using DBI::Profile -it will appear in the profile data. +The C<$statement> here is an arbitrary statement or name you want to +provide as identity of your data. If you're using DBI::Profile it will +appear in the profile data. -Generally it's expected that you are preparing a statement handle -as if a C statement happened. =item * -C<$data> is a reference to the data you are providing, given as an array of arrays. +C<$data> is a reference to the data you are providing, given as an array +of arrays. =item * -C<$names> is a reference an array of column names for the C<$data> you are providing. -The number and order should match the number and ordering of the C<$data> columns. +C<$names> is a reference an array of column names for the C<$data> you +are providing. The number and order should match the number and +ordering of the C<$data> columns. =item * -C<%attr> is a hash of other standard DBI attributes that you might pass to a prepare statement. +C<%attr> is a hash of other standard DBI attributes that you might pass +to a prepare statement. -Currently only NAME, TYPE, and PRECISION are supported. +Currently only NAME, TYPE, and PRECISION are supported. TYPE defaults +to SQL_VARCHAR. PRECISION will be lazily computed if not supplied. =back =head1 BUGS -Using this module to prepare INSERT-like statements is not currently documented. +Using this module to prepare INSERT-like statements is not currently +documented. =head1 AUTHOR AND COPYRIGHT diff -Nru libdbi-perl-1.647/lib/DBI/Changes.pm libdbi-perl-1.652/lib/DBI/Changes.pm --- libdbi-perl-1.647/lib/DBI/Changes.pm 2025-01-20 08:00:20.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/Changes.pm 2026-08-15 08:50:43.000000000 +0000 @@ -12,6 +12,160 @@ =encoding UTF-8 +=head2 Changes in DBI 1.652 - 15 Aug 2026 + +=over 2 + +=item * + +DBI now requires perl-5.12, driven by CVE's + +=item * + +Fix test for perl configured without threads (caught by perl-5.45.1) + +=item * + +Small doc fix, (issue#155), thanks Dick Franks + +=item * + +Add suggested dependency (issue#145), thanks Dick Franks + +=item * + +Force placeholder limit on :# and :p# too (CVE-2026-73194) + +=item * + +Limit statements to 292 Mb in preparse (CVE-2026-73193) + +=item * + +Add a security policy (issue#174) + +=item * + +Add dbm_updatable_key attribute to DBD::DBM to configure how keys are updated + +=item * + +Fix missing import in DBI::DBD::SqlEngine + +=item * + +Fix !Caller2 caller loss when $^P is set (PR#184) (Thanks Paul) + +=back + +=head2 Changes in DBI 1.651 - 14 Jul 2026 + +=over 2 + +=item * + +Fix inverted comparisons for strings in DBI::SQL::Nano (CVE-2026-15043) + +=item * + +Document that IS NULL matches empty strings in DBI::SQL::Nano + +=item * + +Fix DBD::File to ensure that the table is not a symlink outside of f_dir (CVE-2026-15392) + +=item * + +Fix an out-of-bounds error when a statement handle has no fields but the source row is not empty (CVE-2026-60082) + +=item * + +Add an overridable upper bound $MAX_PATH_DEPTH for DBI::ProfileData (CVE-2026-60081) + +=item * + +*** WARNING: Next release will require perl-5.12 *** + +=back + +=head2 Changes in DBI 1.650 - 06 Jul 2026 + +=over 2 + +=item * + +Set a hard limit of 99999 on '?' placeholders (CVE-2026-14739) + +=item * + +Fix out-of-bounds read in preparse of SQL that starts with a comment (CVE-2026-14740) + +=item * + +Fix code injection via Profile DSN attribute or DBI_PROFILE variable (CVE-2026-14380) + +=item * + +Update dbipport.h to Devel::PPPort-3.73 + +=item * + +Require Test::More 0.96 (tests will otherwise fail on pristine perl-5.12) + +=back + +=head2 Changes in DBI 1.649 - 22 Jun 2026 + +=over 2 + +=item * + +Extra Cwd::abs_path required for Windows + +=back + +=head2 Changes in DBI 1.648 - 04 Jun 2026 + +=over 2 + +=item * + +Correct sprintf usage for trace_msg (issue#132) + +=item * + +Add DBIXS_VERSION & DBIXS_RELEASE to dbixs_rev.h + +=item * + +Remove -Wbad-function-cast + +=item * + +Fix possible stack overflow (old issue already noted by Tim) (CVE-2026-9698) + +=item * + +Do not allow table source locations outside explicit given folders + +=item * + +DBD::Sponge PRECISION handling (pr#12, pilcrow) + +=item * + +Fix possible buffer overflow in preparse (CVE-2026-10879) + +=item * + +Skip mismatching .so's (Greg, PR#81) + +=item * + +Fix cast warning in dbi_get_state (Greg, PR#182) + +=back + =head2 Changes in DBI 1.647 - 20 Jan 2025 =over 2 @@ -44,7 +198,7 @@ =item * -RT tickets moved to github issues (rwfranks++) +RT tickets moved to github issues (Dick Franks++) - All RT tickets now marked as resolved with reference to GitHub issue =item * diff -Nru libdbi-perl-1.647/lib/DBI/DBD/Metadata.pm libdbi-perl-1.652/lib/DBI/DBD/Metadata.pm --- libdbi-perl-1.647/lib/DBI/DBD/Metadata.pm 2024-08-13 12:11:45.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/DBD/Metadata.pm 2026-07-14 11:22:01.000000000 +0000 @@ -103,7 +103,7 @@ { my ($dsn, $user, $pass, $driver) = @_ ? @_ : @ARGV; my $dbh = DBI->connect($dsn, $user, $pass, {RaiseError=>1}); - $driver = "" unless defined $driver; + $driver //= ""; print <connect($dsn, $user, $pass, {AutoCommit=>1, RaiseError=>1}); - $driver = "" unless defined $driver; + $driver //= ""; print <SUPER::driver($attr); ... @@ -116,8 +115,7 @@ package DBD::DBM::dr; our @ISA = qw(DBI::DBD::SqlEngine::dr); - sub connect ($$;$$$) - { + sub connect ($$;$$$) { ... } @@ -200,8 +198,7 @@ use base qw( DBI::DBD::SqlEngine ); - sub driver - { + sub driver { my ( $class, $attr ) = @_; ... my $drh = $class->SUPER::driver( $attr ); @@ -251,8 +248,7 @@ package DBD::Foo::db; our @ISA = qw(DBI::DBD::SqlEngine::db); - sub init_default_attributes - { + sub init_default_attributes { my ($dbh, $phase) = @_; $dbh->SUPER::init_default_attributes($phase); ...; # own setup code, maybe separated by phases @@ -628,14 +624,12 @@ package DBI::DBD::SqlEngine::TableSource; - sub data_sources ($;$) - { + sub data_sources ($;$) { my ( $class, $drh, $attrs ) = @_; ... } - sub avail_tables - { + sub avail_tables { my ( $class, $drh ) = @_; ... } @@ -675,8 +669,7 @@ package DBI::DBD::SqlEngine::DataSource; - sub complete_table_name ($$;$) - { + sub complete_table_name ($$;$) { my ( $self, $meta, $table, $respect_case ) = @_; ... } @@ -705,8 +698,7 @@ package DBI::DBD::SqlEngine::DataSource; - sub open_data ($) - { + sub open_data ($) { my ( $self, $meta, $attrs, $flags ) = @_; ... } @@ -835,12 +827,12 @@ The module DBI::DBD::SqlEngine is currently maintained by -H.Merijn Brand < h.m.brand at xs4all.nl > and +H.Merijn Brand < hmbrand at cpan.org > and Jens Rehsack < rehsack at googlemail.com > =head1 COPYRIGHT AND LICENSE -Copyright (C) 2010 by H.Merijn Brand & Jens Rehsack +Copyright (C) 2010-2026 by H.Merijn Brand & Jens Rehsack All rights reserved. diff -Nru libdbi-perl-1.647/lib/DBI/DBD/SqlEngine/HowTo.pod libdbi-perl-1.652/lib/DBI/DBD/SqlEngine/HowTo.pod --- libdbi-perl-1.647/lib/DBI/DBD/SqlEngine/HowTo.pod 2024-08-19 07:15:38.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/DBD/SqlEngine/HowTo.pod 2026-07-13 08:45:37.000000000 +0000 @@ -99,14 +99,13 @@ are valid and mutable or immutable. Tell them by adding C to your db class: - sub init_valid_attributes - { + sub init_valid_attributes { my $dbh = $_[0]; $dbh->SUPER::init_valid_attributes (); $dbh->{foo_valid_attrs} = { - foo_version => 1, # contains version of this driver + foo_version => 1, # contains version of this driver foo_valid_attrs => 1, # contains the valid attributes of foo drivers foo_readonly_attrs => 1, # contains immutable attributes of foo drivers foo_bar => 1, # contains the bar attribute @@ -115,7 +114,7 @@ foo_manager_type => 1, # contains the manager class of the driver instance }; $dbh->{foo_readonly_attrs} = { - foo_version => 1, # ensure no-one modifies the driver version + foo_version => 1, # ensure no-one modifies the driver version foo_valid_attrs => 1, # do not permit one to add more valid attributes ... foo_readonly_attrs => 1, # ... or make the immutable mutable foo_manager => 1, # manager is set internally only @@ -127,16 +126,14 @@ Woooho - but now the user cannot assign new managers? This is intended, overwrite C to handle it! - sub STORE ($$$) - { + sub STORE ($$$) { my ( $dbh, $attrib, $value ) = @_; $dbh->SUPER::STORE( $attrib, $value ); # we're still alive, so no exception is thrown ... # by DBI::DBD::SqlEngine::db::STORE - if ( $attrib eq "foo_manager_type" ) - { + if ( $attrib eq "foo_manager_type" ) { $dbh->{foo_manager} = $dbh->{foo_manager_type}->new(); # ... probably correct some states based on the new # foo_manager_type - see DBD::Sys for an example @@ -147,20 +144,17 @@ a C. Well, no - there're two places where you can initialize defaults: - sub init_default_attributes - { + sub init_default_attributes { my ($dbh, $phase) = @_; $dbh->SUPER::init_default_attributes($phase); - if( 0 == $phase ) - { + if( 0 == $phase ) { # init all attributes which have no knowledge about # user settings from DSN or the attribute hash $dbh->{foo_manager_type} = "DBD::Foo::Manager"; } - elsif( 1 == $phase ) - { + elsif( 1 == $phase ) { # init phase with more knowledge from DSN or attribute # hash $dbh->{foo_manager} = $dbh->{foo_manager_type}->new(); @@ -205,14 +199,13 @@ This interface is provided when derived DBD's define following in C (re-capture L): - sub init_valid_attributes - { + sub init_valid_attributes { my $dbh = $_[0]; $dbh->SUPER::init_valid_attributes (); $dbh->{foo_valid_attrs} = { - foo_version => 1, # contains version of this driver + foo_version => 1, # contains version of this driver foo_valid_attrs => 1, # contains the valid attributes of foo drivers foo_readonly_attrs => 1, # contains immutable attributes of foo drivers foo_bar => 1, # contains the bar attribute @@ -222,7 +215,7 @@ foo_meta => 1, # contains the public interface to modify table meta attributes }; $dbh->{foo_readonly_attrs} = { - foo_version => 1, # ensure no-one modifies the driver version + foo_version => 1, # ensure no-one modifies the driver version foo_valid_attrs => 1, # do not permit one to add more valid attributes ... foo_readonly_attrs => 1, # ... or make the immutable mutable foo_manager => 1, # manager is set internally only @@ -269,8 +262,7 @@ Any further action which needs to be taken can handled in C: - sub table_meta_attr_changed - { + sub table_meta_attr_changed { my ($class, $meta, $attrib, $value) = @_; ... $class->SUPER::table_meta_attr_changed ($meta, $attrib, $value); @@ -306,12 +298,12 @@ The module DBI::DBD::SqlEngine is currently maintained by -H.Merijn Brand < h.m.brand at xs4all.nl > and +H.Merijn Brand < hmbrand at cpan.org > and Jens Rehsack < rehsack at googlemail.com > =head1 COPYRIGHT AND LICENSE -Copyright (C) 2010 by H.Merijn Brand & Jens Rehsack +Copyright (C) 2010-2026 by H.Merijn Brand & Jens Rehsack All rights reserved. diff -Nru libdbi-perl-1.647/lib/DBI/DBD/SqlEngine.pm libdbi-perl-1.652/lib/DBI/DBD/SqlEngine.pm --- libdbi-perl-1.647/lib/DBI/DBD/SqlEngine.pm 2025-01-17 10:04:15.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/DBD/SqlEngine.pm 2026-08-15 08:50:43.000000000 +0000 @@ -10,7 +10,7 @@ # # The original author is Jochen Wiedmann. # -# Copyright (C) 2009-2020 by H.Merijn Brand & Jens Rehsack +# Copyright (C) 2009-2026 by H.Merijn Brand & Jens Rehsack # Copyright (C) 2004 by Jeff Zucker # Copyright (C) 1998 by Jochen Wiedmann # @@ -20,7 +20,7 @@ # General Public License or the Artistic License, as specified in # the Perl README file. -require 5.008; +require 5.012; use strict; @@ -207,7 +207,7 @@ my @ordered_attr = map { $_->[0] } sort { $a->[1] <=> $b->[1] } - map { [ $_, defined $order{$_} ? $order{$_} : 50 ] } + map { [ $_, $order{$_} // 50 ] } keys %$attr; # initialize given attributes ... lower weighted before higher weighted @@ -275,6 +275,7 @@ use warnings; use Carp; +use Scalar::Util qw( refaddr ); if ( eval { require Clone; } ) { @@ -298,7 +299,7 @@ my ( $dbh, $attr, @other ) = @_; my $drh = $dbh->{Driver}; # XXX proxy issues? ref($attr) eq 'HASH' or $attr = {}; - defined( $attr->{sql_table_source} ) or $attr->{sql_table_source} = $dbh->{sql_table_source}; + $attr->{sql_table_source} //= $dbh->{sql_table_source}; return $drh->data_sources( $attr, @other ); } @@ -1671,10 +1672,9 @@ use base qw(DBI::DBD::SqlEngine); - sub driver - { + sub driver { ... - my $drh = $proto->SUPER::driver($attr); + my $drh = $proto->SUPER::driver ($attr); ... return $drh->{class}; } @@ -1982,11 +1982,10 @@ Signature: - sub sql_get_meta ($$) - { + sub sql_get_meta ($$) { my ($table_name, $attrib) = @_; ... - } + } Returns the value of a meta attribute set for a specific table, if any. See L for the possible attributes. @@ -1999,11 +1998,10 @@ Signature: - sub sql_set_meta ($$$) - { + sub sql_set_meta ($$$) { my ($table_name, $attrib, $value) = @_; ... - } + } Sets the value of a meta attribute set for a specific table. See L for the possible attributes. @@ -2016,11 +2014,10 @@ Signature: - sub sql_clear_meta ($) - { + sub sql_clear_meta ($) { my ($table_name) = @_; ... - } + } Clears the table specific meta information in the private storage of the dbh. @@ -2034,47 +2031,45 @@ package DBI::DBD::SqlEngine::TableSource; - sub data_sources ($;$) - { - my ( $class, $drh, $attrs ) = @_; + sub data_sources ($;$) { + my ($class, $drh, $attrs) = @_; ... - } + } - sub avail_tables - { + sub avail_tables { my ( $class, $drh ) = @_; ... - } + } The C method is called when the user invokes any of the following: - @ary = DBI->data_sources($driver); - @ary = DBI->data_sources($driver, \%attr); + @ary = DBI->data_sources ($driver); + @ary = DBI->data_sources ($driver, \%attr); - @ary = $dbh->data_sources(); - @ary = $dbh->data_sources(\%attr); + @ary = $dbh->data_sources (); + @ary = $dbh->data_sources (\%attr); The C method is called when the user invokes any of the following: - @names = $dbh->tables( $catalog, $schema, $table, $type ); + @names = $dbh->tables ($catalog, $schema, $table, $type); - $sth = $dbh->table_info( $catalog, $schema, $table, $type ); - $sth = $dbh->table_info( $catalog, $schema, $table, $type, \%attr ); + $sth = $dbh->table_info ($catalog, $schema, $table, $type); + $sth = $dbh->table_info ($catalog, $schema, $table, $type, \%attr); - $dbh->func( "list_tables" ); + $dbh->func ("list_tables"); Every time where an C<\%attr> argument can be specified, this C<\%attr> object's C attribute is preferred over the C<$dbh> attribute or the driver default, e.g. - @ary = DBI->data_sources("dbi:CSV:", { + @ary = DBI->data_sources ("dbi:CSV:", { f_dir => "/your/csv/tables", # note: this class doesn't comes with DBI sql_table_source => "DBD::File::Archive::Tar::TableSource", # scan tarballs instead of directories - }); + }); When you're going to implement such a DBD::File::Archive::Tar::TableSource class, remember to add correct attributes (including C @@ -2092,11 +2087,10 @@ package DBI::DBD::SqlEngine::DataSource; - sub complete_table_name ($$;$) - { - my ( $self, $meta, $table, $respect_case ) = @_; + sub complete_table_name ($$;$) { + my ($self, $meta, $table, $respect_case) = @_; ... - } + } The method C is called when first setting up the I for a table: @@ -2122,11 +2116,10 @@ package DBI::DBD::SqlEngine::DataSource; - sub open_data ($) - { - my ( $self, $meta, $attrs, $flags ) = @_; + sub open_data ($) { + my ($self, $meta, $attrs, $flags) = @_; ... - } + } After the method C has been run successfully, the table's meta information are in a state which allows the table's data accessor methods @@ -2199,14 +2192,14 @@ This module is currently maintained by -H.Merijn Brand < h.m.brand at xs4all.nl > and +H.Merijn Brand < hmbrand at cpan.org > and Jens Rehsack < rehsack at googlemail.com > The original authors are Jochen Wiedmann and Jeff Zucker. =head1 COPYRIGHT AND LICENSE - Copyright (C) 2009-2020 by H.Merijn Brand & Jens Rehsack + Copyright (C) 2009-2026 by H.Merijn Brand & Jens Rehsack Copyright (C) 2004-2009 by Jeff Zucker Copyright (C) 1998-2004 by Jochen Wiedmann diff -Nru libdbi-perl-1.647/lib/DBI/DBD.pm libdbi-perl-1.652/lib/DBI/DBD.pm --- libdbi-perl-1.647/lib/DBI/DBD.pm 2025-01-17 10:03:29.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/DBD.pm 2026-07-14 09:54:24.000000000 +0000 @@ -30,7 +30,7 @@ Please read the B documentation first and fully. Then look at the implementation of some high-profile and regularly maintained drivers like -DBD::Oracle, DBD::ODBC, DBD::Pg etc. (Those are no no particular order.) +DBD::Oracle, DBD::ODBC, DBD::Pg etc. (Those are in no particular order.) Then reread the B specification and the code of those drivers again as you're reading this. It'll help. Where this document and the driver code @@ -3418,7 +3418,7 @@ foreach my $match (@test_matches) { next TEST if $test !~ $match; } - my $usethr = ($test =~ /(\d+|\b)thr/ && $] >= 5.008 && $Config{useithreads}); + my $usethr = ($test =~ /(\d+|\b)thr/ && $Config{useithreads}); my $v_test = "t/zv${v_type}_$test"; my $v_perl = ($test =~ /taint/) ? "perl -wT" : "perl -w"; printf "%s %s\n", $v_test, ($usethr) ? "(use threads)" : ""; diff -Nru libdbi-perl-1.647/lib/DBI/Gofer/Transport/Base.pm libdbi-perl-1.652/lib/DBI/Gofer/Transport/Base.pm --- libdbi-perl-1.647/lib/DBI/Gofer/Transport/Base.pm 2013-06-03 17:12:01.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/Gofer/Transport/Base.pm 2026-07-14 11:24:01.000000000 +0000 @@ -139,7 +139,7 @@ sub trace_msg { my ($self, $msg, $min_level) = @_; - $min_level = 1 unless defined $min_level; + $min_level //= 1; # transport trace level can override DBI's trace level $min_level = 0 if $self->trace >= $min_level; return DBI->trace_msg("gofer ".$msg, $min_level); diff -Nru libdbi-perl-1.647/lib/DBI/Profile.pm libdbi-perl-1.652/lib/DBI/Profile.pm --- libdbi-perl-1.647/lib/DBI/Profile.pm 2025-01-17 09:56:44.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/Profile.pm 2026-07-13 08:46:08.000000000 +0000 @@ -679,6 +679,7 @@ use Exporter (); use UNIVERSAL (); use Carp; +use Module::Load (); use DBI qw(dbi_time dbi_profile dbi_profile_merge_nodes dbi_profile_merge); @@ -738,7 +739,7 @@ push @p, DBIprofile_Statement if $element & 0x02; push @p, DBIprofile_MethodName if $element & 0x04; push @p, DBIprofile_MethodClass if $element & 0x08; - push @p, '!Caller2' if $element & 0x10; + push @p, '!Caller2' if $element & 0x10; push @Path, ($reverse ? reverse @p : @p); } elsif ($element =~ m/^&(\w.*)/) { @@ -758,7 +759,9 @@ } } - eval "require $package" if $package; # silently ignores errors + eval { + Module::Load::load $package if $package; # silently ignores errors + }; $package ||= $class; return $package->new(Path => \@Path, @args); diff -Nru libdbi-perl-1.647/lib/DBI/ProfileData.pm libdbi-perl-1.652/lib/DBI/ProfileData.pm --- libdbi-perl-1.647/lib/DBI/ProfileData.pm 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/ProfileData.pm 2026-07-14 06:38:30.000000000 +0000 @@ -15,6 +15,8 @@ This module can also be used to roll your own profile analysis: + local $DBI::ProfileData::MAX_PATH_DEPTH = 256; # override the maximum path depth of files + # load data from dbi.prof $prof = DBI::ProfileData->new(File => "dbi.prof"); @@ -78,6 +80,8 @@ use DBI::Profile qw(dbi_profile_merge); +our $MAX_PATH_DEPTH = 256; + # some constants for use with node data arrays sub COUNT () { 0 }; sub TOTAL () { 1 }; @@ -287,6 +291,13 @@ # it's a key my ($key, $index) = ($2, $1 - 1); + # $index comes straight from the (untrusted) profile file; a + # single huge value would expand @path into a giant sparse + # array that is then join()ed and copied per data line, giving + # a tiny-file -> large-memory amplification. Bound it. + croak("Invalid path index at $filename line $.: $1 (You may need to adjust \$MAX_PATH_DEPTH)") + if $index < 0 || $index > $MAX_PATH_DEPTH; + $#path = $index; # truncate path to new length $path[$index] = unescape_key($key); # place new key at end diff -Nru libdbi-perl-1.647/lib/DBI/ProxyServer.pm libdbi-perl-1.652/lib/DBI/ProxyServer.pm --- libdbi-perl-1.647/lib/DBI/ProxyServer.pm 2025-01-17 10:00:11.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/ProxyServer.pm 2026-07-13 08:45:48.000000000 +0000 @@ -46,8 +46,8 @@ { my $o = \%DEFAULT_SERVER_OPTIONS; $o->{'chroot'} = undef, # To be used in the initfile, - # after loading the required - # DBI drivers. + # after loading the required + # DBI drivers. $o->{'clients'} = [ { 'mask' => '.*', 'accept' => 1, diff -Nru libdbi-perl-1.647/lib/DBI/PurePerl.pm libdbi-perl-1.652/lib/DBI/PurePerl.pm --- libdbi-perl-1.647/lib/DBI/PurePerl.pm 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/PurePerl.pm 2026-07-14 11:27:03.000000000 +0000 @@ -110,7 +110,7 @@ use constant IMA_FUNC_REDIRECT => 0x0002; #/* is $h->func(..., "method")*/ use constant IMA_KEEP_ERR => 0x0004; #/* don't reset err & errstr */ use constant IMA_KEEP_ERR_SUB => 0x0008; #/* '' if in nested call */ -use constant IMA_NO_TAINT_IN => 0x0010; #/* don't check for tainted args*/ +use constant IMA_NO_TAINT_IN => 0x0010; #/* don't check for tainted args*/ use constant IMA_NO_TAINT_OUT => 0x0020; #/* don't taint results */ use constant IMA_COPY_UP_STMT => 0x0040; #/* copy sth Statement to dbh */ use constant IMA_END_WORK => 0x0080; #/* set on commit & rollback */ @@ -959,7 +959,7 @@ } sub trace_msg { my ($h, $msg, $minlevel)=@_; - $minlevel = 1 unless defined $minlevel; + $minlevel //= 1; return unless $minlevel <= ($DBI::dbi_debug & 0xF); print $DBI::tfh $msg; return 1; diff -Nru libdbi-perl-1.647/lib/DBI/SQL/Nano.pm libdbi-perl-1.652/lib/DBI/SQL/Nano.pm --- libdbi-perl-1.647/lib/DBI/SQL/Nano.pm 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/SQL/Nano.pm 2026-07-14 11:25:45.000000000 +0000 @@ -668,8 +668,8 @@ return 1 if ( !defined $val1 or $val1 eq '' ); return 0; } - $val1 = '' unless ( defined $val1 ); - $val2 = '' unless ( defined $val2 ); + $val1 //= ''; + $val2 //= ''; if ( $op =~ /LIKE|CLIKE/i ) { $val2 = quotemeta($val2); @@ -693,8 +693,8 @@ if ( $op eq '>' ) { return $val1 gt $val2; } if ( $op eq '=' ) { return $val1 eq $val2; } if ( $op eq '<>' ) { return $val1 ne $val2; } - if ( $op eq '<=' ) { return $val1 ge $val2; } - if ( $op eq '>=' ) { return $val1 le $val2; } + if ( $op eq '<=' ) { return $val1 le $val2; } + if ( $op eq '>=' ) { return $val1 ge $val2; } } } @@ -928,6 +928,7 @@ * op may be one of: < > >= <= = <> LIKE CLIKE IS * CLIKE is a case insensitive LIKE + * IS NULL matches against NULL and empty strings order_clause ::= column_name [ASC|DESC] * a single column optional ORDER BY clause is supported diff -Nru libdbi-perl-1.647/lib/DBI/W32ODBC.pm libdbi-perl-1.652/lib/DBI/W32ODBC.pm --- libdbi-perl-1.647/lib/DBI/W32ODBC.pm 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/lib/DBI/W32ODBC.pm 2026-07-13 08:48:14.000000000 +0000 @@ -150,7 +150,7 @@ } sub finish { - shift->Close; ## uncommented this line + shift->Close; ## uncommented this line } # --- @@ -163,11 +163,11 @@ } sub disconnect { - my ($h) = shift; ## this will kill all the statement handles + my ($h) = shift; ## this will kill all the statement handles foreach (@{$h->{'___sths'}}) { ## created for a specific connection - $_->Close if $_->{DSN}; ## - } ## - $h->Close; ## + $_->Close if $_->{DSN}; ## + } ## + $h->Close; ## } sub err { diff -Nru libdbi-perl-1.647/lib/Win32/DBIODBC.pm libdbi-perl-1.652/lib/Win32/DBIODBC.pm --- libdbi-perl-1.647/lib/Win32/DBIODBC.pm 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/lib/Win32/DBIODBC.pm 2026-07-13 08:49:08.000000000 +0000 @@ -33,13 +33,13 @@ my $self= {}; $_=$connect_line; - /^(DSN=)(.*)(;UID=)(.*)(;PWD=)(.*)(;)$/; + /^(DSN=)(.*)(;UID=)(.*)(;PWD=)(.*)(;)$/; - #---- DBI CONNECTION VARIABLES + #---- DBI CONNECTION VARIABLES - $self->{ODBC_DSN}=$2; - $self->{ODBC_UID}=$4; - $self->{ODBC_PWD}=$6; + $self->{ODBC_DSN}=$2; + $self->{ODBC_UID}=$4; + $self->{ODBC_PWD}=$6; #---- DBI CONNECTION VARIABLES $self->{DBI_DBNAME}=$self->{ODBC_DSN}; @@ -61,8 +61,8 @@ #EMU --- $db->Sql('SELECT * FROM DUAL'); sub Sql { - my $self= shift; - my $SQL_statment=shift; + my $self= shift; + my $SQL_statment=shift; # print " SQL : $SQL_statment \n"; @@ -94,7 +94,7 @@ } # [R] provide compatibility with Win32::ODBC's way of identifying erroneous SQL statements - return ($self->{'DBI_ERR'})?1:undef; + return ($self->{'DBI_ERR'})?1:undef; # -[R]- } @@ -102,20 +102,20 @@ #EMU --- $db->FetchRow()) sub FetchRow { - my $self= shift; + my $self= shift; - my $sth=$self->{'DBI_STH'}; - if ($sth) + my $sth=$self->{'DBI_STH'}; + if ($sth) { - my @row=$sth->fetchrow_array; - $self->{'DBI_ROW'}=\@row; + my @row=$sth->fetchrow_array; + $self->{'DBI_ROW'}=\@row; - if (scalar(@row)>0) - { + if (scalar(@row)>0) + { #-- the row of result is not nul #-- return something nothing will be return else return 1; - } + } } return undef; } @@ -137,15 +137,15 @@ #EMU --- %record = $db->DataHash; sub DataHash { - my $self= shift; + my $self= shift; - my $p_name=$self->{'DBI_NAME'}; - my $p_row=$self->{'DBI_ROW'}; + my $p_name=$self->{'DBI_NAME'}; + my $p_row=$self->{'DBI_ROW'}; - my @name=@$p_name; - my @row=@$p_row; + my @name=@$p_name; + my @row=@$p_row; - my %DataHash; + my %DataHash; #print @name; print "\n"; print @row; # [R] new code that seems to work consistent with Win32::ODBC while (@name) @@ -174,23 +174,23 @@ # } # -[R]- - #--- Return Hash - return %DataHash; + #--- Return Hash + return %DataHash; } #EMU --- $db->Error() sub Error { - my $self= shift; + my $self= shift; - if ($self->{'DBI_ERR'} ne '') - { + if ($self->{'DBI_ERR'} ne '') + { #--- Return error message $self->{'DBI_ERRSTR'}; - } + } - #-- else good no error message + #-- else good no error message } # [R] provide compatibility with Win32::ODBC's Close() method. diff -Nru libdbi-perl-1.647/t/05concathash.t libdbi-perl-1.652/t/05concathash.t --- libdbi-perl-1.647/t/05concathash.t 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/t/05concathash.t 2026-07-13 08:46:21.000000000 +0000 @@ -135,7 +135,7 @@ print "\n"; cmpthese(200_000, { - NotNeat => sub {DBI::_concat_hash_sorted( + NotNeat => sub {DBI::_concat_hash_sorted( $simple_hash, "=", ":",1,undef); }, Neat => sub {DBI::_concat_hash_sorted( diff -Nru libdbi-perl-1.647/t/06attrs.t libdbi-perl-1.652/t/06attrs.t --- libdbi-perl-1.647/t/06attrs.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/06attrs.t 2026-07-13 08:46:31.000000000 +0000 @@ -101,7 +101,7 @@ is($dbh->state, 'S1000', '... checking $dbh->state'); ok($dbh->{Executed}, '... checking Executed attribute for dbh'); # even though it failed -$dbh->{Executed} = 0; # reset(able) +$dbh->{Executed} = 0; # reset(able) cmp_ok($dbh->{Executed}, '==', 0, '... checking Executed attribute for dbh (after reset)'); cmp_ok($dbh->{ErrCount}, '==', 1, '... checking ErrCount attribute for dbh (after error was generated)'); diff -Nru libdbi-perl-1.647/t/07kids.t libdbi-perl-1.652/t/07kids.t --- libdbi-perl-1.647/t/07kids.t 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/t/07kids.t 2026-07-22 12:20:58.000000000 +0000 @@ -10,7 +10,6 @@ BEGIN { plan skip_all => '$h->{Kids} attribute not supported for DBI::PurePerl' if $DBI::PurePerl && $DBI::PurePerl; # doubled to avoid typo warning - plan tests => 20; } ## ---------------------------------------------------------------------------- @@ -100,3 +99,4 @@ cmp_ok( $drh->{Kids}, '==', 0, '... driver handle has 0 Kid(s) after undef $dbh'); cmp_ok( $drh->{ActiveKids}, '==', 0, '... driver handle has 0 ActiveKid(s) after undef $dbh'); +done_testing; diff -Nru libdbi-perl-1.647/t/10examp.t libdbi-perl-1.652/t/10examp.t --- libdbi-perl-1.647/t/10examp.t 2024-08-13 12:11:45.000000000 +0000 +++ libdbi-perl-1.652/t/10examp.t 2026-07-27 08:06:08.000000000 +0000 @@ -14,7 +14,7 @@ require File::Spec; require VMS::Filespec if $^O eq 'VMS'; -use Test::More tests => 242; +use Test::More; do { # provide some protection against growth in size of '.' during the test @@ -114,6 +114,12 @@ eval { $dbh->commit('dummy') }; ok($@ =~ m/DBI commit: invalid number of arguments:/, $@) unless $DBI::PurePerl && ok(1); +#my $long_usage_method = 'examplep_' . ('U' x 260); +#DBD::ExampleP::db->install_method( +# $long_usage_method, { U => [ 1, 1, '' ] }, +#); +#eval { $dbh->$long_usage_method('dummy') }; +#like($@, qr/invalid number of arguments.*Usage:/s, 'long usage diagnostic is safe'); ok($dbh->ping, "ping should return true"); @@ -637,4 +643,6 @@ ok(!$dbh->{Active}); ok(!$dbh->ping, "ping should return false after disconnect"); +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/11fetch.t libdbi-perl-1.652/t/11fetch.t --- libdbi-perl-1.647/t/11fetch.t 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/t/11fetch.t 2026-07-22 12:21:15.000000000 +0000 @@ -13,8 +13,6 @@ $Data::Dumper::Sortkeys = 1; $Data::Dumper::Quotekeys = 0; -plan tests => 24; - my $dbh = DBI->connect("dbi:Sponge:foo","","", { PrintError => 0, RaiseError => 1, @@ -120,5 +118,6 @@ Benchmark::timethis(10, sub { go(\@perf)->fetchall_hashref([ 'C1','C2','C3' ]) }); } +done_testing; 1; # end diff -Nru libdbi-perl-1.647/t/13taint.t libdbi-perl-1.652/t/13taint.t --- libdbi-perl-1.647/t/13taint.t 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/t/13taint.t 2026-07-27 08:00:58.000000000 +0000 @@ -6,7 +6,6 @@ use Cwd; use strict; - $^W = 1; $| = 1; @@ -29,8 +28,6 @@ plan skip_all => "Taint attribute tests require taint mode (perl -T)" unless is_tainted($^X); plan skip_all => "Taint attribute tests not functional with DBI_AUTOPROXY" if $ENV{DBI_AUTOPROXY}; -plan tests => 36; - # get a dir always readable on all platforms my $dir = getcwd() || cwd(); $dir = VMS::Filespec::unixify($dir) if $^O eq 'VMS'; @@ -114,6 +111,9 @@ ok($@ =~ /Insecure dependency/, $@); eval { $csr_a->execute($tainted_dot); 1; }; ok($@ =~ /Insecure dependency/, $@); +eval { $dbh->func($tainted_dot, 'M' x 180); 1; }; +ok($@ =~ /Insecure dependency.*parameter 1/s, + 'long func method name is safely included in taint rejection'); undef $@; $dbh->{'TaintIn'} = $csr_a->{'TaintIn'} = 0; @@ -130,4 +130,6 @@ $dbh->disconnect; +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/14utf8.t libdbi-perl-1.652/t/14utf8.t --- libdbi-perl-1.647/t/14utf8.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/14utf8.t 2026-07-22 12:21:43.000000000 +0000 @@ -15,9 +15,7 @@ plan skip_all => "Unable to load required module ($@)" unless defined &_utf8_on; -plan tests => 16; - -$dbh = DBI->connect("dbi:Sponge:foo","","", { +my $dbh = DBI->connect("dbi:Sponge:foo","","", { PrintError => 0, RaiseError => 1, }); @@ -70,4 +68,6 @@ $sth->finish; +done_testing; + # end diff -Nru libdbi-perl-1.647/t/31methcache.t libdbi-perl-1.652/t/31methcache.t --- libdbi-perl-1.647/t/31methcache.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/31methcache.t 2026-07-22 12:18:09.000000000 +0000 @@ -3,33 +3,20 @@ # check that the inner-method lookup cache works # (or rather, check that it doesn't cache things when it shouldn't) -BEGIN { eval "use threads;" } # Must be first -my $use_threads_err = $@; +my $use_threads_err; +BEGIN { eval "use threads;"; $use_threads_err = $@; } # Must be first use Config qw(%Config); -# With this test code and threads, 5.8.1 has issues with freeing freed -# scalars, while 5.8.9 doesn't; I don't know about in-between - DAPM my $has_threads = $Config{useithreads}; die $use_threads_err if $has_threads && $use_threads_err; use Test::More; -# weaken itself is buggy on 5.8.1 (magic killbackrefs panic -# triggered by threads, fixed in 5.8.2, but 5.8.2 has other -# issues that should have been fixed in 5.8.3, but 5.8.4 .. -# 5.8.6 still fail where 5.8.7 PASSes -if ($has_threads && $] < 5.008007) { - plan skip_all => "Test will fail in threaded perl <= 5.8.6"; -} -if ($] >= 5.010000 && $] < 5.012000) { - plan skip_all => "Test will fail in perl-5.10.x"; -} - use strict; $|=1; $^W=1; -plan tests => 49; +$has_threads or diag("No threads available in this perl"); use_ok( 'DBI' ); @@ -130,33 +117,32 @@ run_tests("plain", new_handle()); -SKIP: { - skip "no threads / perl < 5.8.9", 12 unless $has_threads; +if ($has_threads) { # only enable this when handles are allowed to be shared across threads #{ # my @h = new_handle(); # threads->new(sub { run_tests("threads", @h) })->join; #} threads->new(sub { run_tests("threads-h", new_handle()) })->join; -}; -# using weaken attaches magic to the CV; see whether this interferes -# with the cache magic + # using weaken attaches magic to the CV; see whether this interferes + # with the cache magic +} use Scalar::Util qw(weaken); my $fetch_ref = \&DBI::st::fetch; weaken $fetch_ref; run_tests("magic", new_handle()); -SKIP: { - skip "no threads / perl < 5.8.9", 12 unless $has_threads; - +if ($has_threads) { # only enable this when handles are allowed to be shared across threads #{ # my @h = new_handle(); # threads->new(sub { run_tests("threads", @h) })->join; #} threads->new(sub { run_tests("magic threads-h", new_handle()) })->join; -}; +} + +done_testing; 1; diff -Nru libdbi-perl-1.647/t/35thrclone.t libdbi-perl-1.652/t/35thrclone.t --- libdbi-perl-1.647/t/35thrclone.t 2024-08-13 12:11:45.000000000 +0000 +++ libdbi-perl-1.652/t/35thrclone.t 2026-07-22 12:22:02.000000000 +0000 @@ -17,14 +17,7 @@ die $use_threads_err if $use_threads_err; # need threads } -BEGIN { - if ($] >= 5.010000 && $] < 5.010001) { - plan skip_all => "Threading bug in perl 5.10.0 fixed in 5.10.1"; - } -} - my $threads = 4; -plan tests => 4 + 4 * $threads; { package threads_sub; @@ -51,13 +44,8 @@ isa_ok( $dbh, 'DBI::db' ); isnt($dbh, $dbh_parent, '... new $dbh is not the same instance as $dbh_parent'); - SKIP: { - # skip seems broken with threads (5.8.3) - # skip "Kids attribute not supported under DBI::PurePerl", 1 if $DBI::PurePerl; - - cmp_ok($dbh->{Driver}->{Kids}, '==', 1, '... the Driver has one Kid') - unless $DBI::PurePerl && ok(1); - } + cmp_ok($dbh->{Driver}->{Kids}, '==', 1, '... the Driver has one Kid') + unless $DBI::PurePerl && ok(1); # RT #77137: a thread created from a thread was crashing the # interpreter @@ -89,4 +77,6 @@ pass('... all tests have passed'); +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/40profile.t libdbi-perl-1.652/t/40profile.t --- libdbi-perl-1.647/t/40profile.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/40profile.t 2026-07-22 12:22:24.000000000 +0000 @@ -20,18 +20,12 @@ plan skip_all => "profiling not supported for DBI::PurePerl" if $DBI::PurePerl; - # tie methods (STORE/FETCH etc) get called different number of times - plan skip_all => "test results assume perl >= 5.8.2" - if $] <= 5.008001; - # clock instability on xen systems is a reasonably common cause of failure # http://www.nntp.perl.org/group/perl.cpan.testers/2009/05/msg3828158.html # so we'll skip automated testing on those systems plan skip_all => "skipping profile tests on xen (due to clock instability)" if $Config{osvers} =~ /xen/ # eg 2.6.18-4-xen-amd64 and $ENV{AUTOMATED_TESTING}; - - plan tests => 60; } $Data::Dumper::Indent = 1; @@ -458,8 +452,53 @@ 'merged time foo/bar'); is($total_time, 2.93, 'merged nodes foo/bar time'); +subtest "CVE-2026-14380" => sub { + + plan tests => 3; + + { + my $marker = sprintf('dbi-test-payload-%1.6f-%u-%u-%u', $], time, $$, 1); + local $ENV{DBI_PROFILE} = payload_for($marker); + my $dbh = eval { + DBI->connect("dbi:Sponge:", "", "", { RaiseError => 0 }) + }; + ok !( -e "/tmp/$marker" ), "ENV DBI_PROFILE payload"; + unlink "/tmp/$marker" if -e "/tmp/$marker"; + } + + { + my $marker = sprintf('dbi-test-payload-%1.6f-%u-%u-%u', $], time, $$, 2); + my $dbh = DBI->connect("dbi:Sponge:", "", "", { RaiseError => 0 }); + eval { + $dbh->{Profile} = payload_for($marker); + }; + ok !( -e "/tmp/$marker" ), "Set Profile payload"; + unlink "/tmp/$marker" if -e "/tmp/$marker"; + } + + { + my $marker = sprintf('dbi-test-payload-%1.6f-%u-%u-%u', $], time, $$, 3); + my $payload = payload_for($marker); + my $dsn = "dbi:Sponge(Profile=>$payload):"; + my $dbh = eval { + DBI->connect($dsn, "", "", { RaiseError => 0 }) + }; + ok !( -e "/tmp/$marker" ), "DSN payload"; + unlink "/tmp/$marker" if -e "/tmp/$marker"; + } + +}; + +done_testing; + exit 0; +sub payload_for { + my ($marker) = @_; + # Single-quoted q{...} so \x2f is literal backslash-x-2-f for split; + # the inner qq(...) re-interprets \x2f = / at eval-time. + return qq{2/system(qq(touch \\x2ftmp\\x2f$marker))}; +} sub sanitize_tree { my $data = shift; diff -Nru libdbi-perl-1.647/t/41prof_dump.t libdbi-perl-1.652/t/41prof_dump.t --- libdbi-perl-1.647/t/41prof_dump.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/41prof_dump.t 2026-07-22 12:22:41.000000000 +0000 @@ -23,8 +23,6 @@ plan skip_all => "skipping profile tests on xen (due to clock instability)" if $Config{osvers} =~ /xen/ # eg 2.6.18-4-xen-amd64 and $ENV{AUTOMATED_TESTING}; - - plan tests => 15; } BEGIN { @@ -102,4 +100,6 @@ require_ok('DBI::ProfileDumper::Apache') } +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/42prof_data.t libdbi-perl-1.652/t/42prof_data.t --- libdbi-perl-1.647/t/42prof_data.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/42prof_data.t 2026-07-22 12:22:54.000000000 +0000 @@ -18,8 +18,6 @@ plan skip_all => "skipping profile tests on xen (due to clock instability)" if $Config{osvers} =~ /xen/ # eg 2.6.18-4-xen-amd64 and $ENV{AUTOMATED_TESTING}; - - plan tests => 31; } BEGIN { @@ -153,4 +151,6 @@ ok(exists $Data->{$sql3}, "Data for '$sql3' should exist") or print Dumper($Data); +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/49dbd_file.t libdbi-perl-1.652/t/49dbd_file.t --- libdbi-perl-1.647/t/49dbd_file.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/49dbd_file.t 2026-05-28 10:18:40.000000000 +0000 @@ -192,12 +192,12 @@ } ok ($sth = $dbh->prepare ("insert into $tbl (txt) values (?)"), "prepare 'insert into $tbl'"); -is ($sth->execute ("Perl rules"), undef, "insert failed intensionally"); +is ($sth->execute ("Perl rules"), undef, "insert failed intentionally"); ok ($sth = $dbh->prepare ("delete from $tbl"), "prepare 'delete from $tbl'"); -is ($sth->execute (), undef, "delete failed intensionally"); +is ($sth->execute (), undef, "delete failed intentionally"); -is ($dbh->do ("drop table $tbl"), undef, "table drop failed intensionally"); +is ($dbh->do ("drop table $tbl"), undef, "table drop failed intentionally"); is (-f $tbl_file, 1, "Test table not removed"); # ==================== ReadWrite again tests ====================== @@ -244,8 +244,7 @@ done_testing (); -sub DBD::File::Table::fetch_row ($$) -{ +sub DBD::File::Table::fetch_row ($$) { my ($self, $data) = @_; my $meta = $self->{meta}; if ($rowidx >= scalar @rows) { @@ -257,8 +256,7 @@ return $self->{row}; } # fetch_row -sub DBD::File::Table::push_names ($$$) -{ +sub DBD::File::Table::push_names ($$$) { my ($self, $data, $row_aryref) = @_; my $meta = $self->{meta}; @tfhl = PerlIO::get_layers ($meta->{fh}); diff -Nru libdbi-perl-1.647/t/50dbm_simple.t libdbi-perl-1.652/t/50dbm_simple.t --- libdbi-perl-1.647/t/50dbm_simple.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/50dbm_simple.t 2026-08-15 08:50:43.000000000 +0000 @@ -105,6 +105,18 @@ [ 2, 'apples' ], [ 1, 'oranges' ], ], + "SELECT * FROM fruit WHERE dVal >= 'oranges' ORDER BY dKey", [ + [ 1, 'oranges' ], + [ 5, 'via placeholders' ], + ], + "SELECT * FROM fruit WHERE dVal <= 'oranges' ORDER BY dVal", [ + [ 3, '' ], + [ 2, 'apples' ], + [ 1, 'oranges' ], + ], + "SELECT * FROM fruit WHERE dVal IS NULL", [ + [ 3, '' ], + ], "DELETE FROM fruit", 4, $dbi_sql_nano ? () : ( "SELECT COUNT(*) FROM fruit", [ [ 0 ] ] ), "DROP TABLE fruit", -1, @@ -264,7 +276,69 @@ my @tables = $sth->fetchall_arrayref; is_deeply( \@tables, [ [] ], "No tables delivered by table_info" ); + # TODO these tests should be run using the database connection parameters + do_update_test( $dbh, $dtype ) unless $using_dbd_gofer; + $dbh->disconnect; + return 1; } + +sub do_update_test { + my ( $dbh, $dtype ) = @_; + + for my $mode ( 0 .. 2 ) { + + note "dbm_updatable_key = $mode"; + $dbh->{dbm_updatable_key} = $mode; + + my $tests = [ + "DROP TABLE IF EXISTS brassica", -1, + "CREATE TABLE brassica (dKey INT, dVal VARCHAR(10))", '0E0', + "INSERT INTO brassica VALUES (1,'neep')", 1, + "INSERT INTO brassica VALUES (2,'kale')", 1, + "UPDATE brassica SET dKey=1 WHERE dKey=2", \$mode, # depends on mode + "DROP TABLE brassica", -1, + ]; + + my $i = 0; + my ( $queries, $expected ) = part { $i++ % 2 } @{$tests}; + + my $idx = 0; + for my $sql ( @{$queries} ) { + + $sql =~ s/\S*brassica/${dtype}_brassica/; # include dbm type in table name + + my $sth = $dbh->prepare($sql); + ok( $sth, "prepare $sql" ) or diag( $dbh->errstr || 'unknown error' ); + + my $expect = $expected->[$idx]; + if ( ref($expect) ) { + + $sth->{PrintError} = 0; + + my $n = $sth->execute(); + + if ( $mode == 2 ) { + ok( !$n, 'execute failed' ); + like $sth->errstr, qr/^Row with PK '1' already exists/, 'execpted error'; + } + else { + # TODO: trap warnings to test when $mode == 1 + is( $n, 1, 'execute' ) or diag( $sth->errstr || 'unknown error' ); + } + + } + else { + my $n = $sth->execute(); + is( $n, $expect, 'execute' ) or diag( $sth->errstr || 'unknown error' ); + } + + $idx++; + } + + } + +} + 1; diff -Nru libdbi-perl-1.647/t/51dbm_file.t libdbi-perl-1.652/t/51dbm_file.t --- libdbi-perl-1.647/t/51dbm_file.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/51dbm_file.t 2026-05-28 11:19:06.000000000 +0000 @@ -4,210 +4,220 @@ use strict; use warnings; +use Cwd (); use File::Copy (); use File::Path; use File::Spec (); use Test::More; -my $using_dbd_gofer = ( $ENV{DBI_AUTOPROXY} || '' ) =~ /^dbi:Gofer.*transport=/i; +my $using_dbd_gofer = ($ENV{DBI_AUTOPROXY} || "") =~ m/^dbi:Gofer.*transport=/i; use DBI; do "./t/lib.pl"; -{ - # test issue reported in RT#99508 +{ # test issue reported in RT#99508 my @msg; my $dbh = eval { - local $SIG{__WARN__} = sub { push @msg, @_ }; - local $SIG{__DIE__} = sub { push @msg, @_ }; + local $SIG{__WARN__} = sub { push @msg => @_ }; + local $SIG{__DIE__} = sub { push @msg => @_ }; DBI->connect ("dbi:DBM:f_dir=./hopefully-doesnt-existst;sql_identifier_case=1;RaiseError=1"); - }; + }; is ($dbh, undef, "Connect failed"); like ("@msg", qr{.*hopefully-doesnt-existst.*}, "Cannot open from non-existing directory with attributes in DSN"); @msg = (); $dbh = eval { - local $SIG{__WARN__} = sub { push @msg, @_ }; - local $SIG{__DIE__} = sub { push @msg, @_ }; + local $SIG{__WARN__} = sub { push @msg => @_ }; + local $SIG{__DIE__} = sub { push @msg => @_ }; DBI->connect ("dbi:DBM:", , undef, undef, { f_dir => "./hopefully-doesnt-existst", sql_identifier_case => 1, RaiseError => 1, - }); - }; + }); + }; is ($dbh, undef, "Connect failed"); like ("@msg", qr{.*hopefully-doesnt-existst}, "Cannot open from non-existing directory with attributes in HASH"); -} + } -my $dir = test_dir(); +my $dir = test_dir (); -my $dbh = DBI->connect( 'dbi:DBM:', undef, undef, { - f_dir => $dir, - sql_identifier_case => 1, # SQL_IC_UPPER - } -); +my $dbh = DBI->connect ("dbi:DBM:", undef, undef, { + f_dir => $dir, + sql_identifier_case => 1, # SQL_IC_UPPER + }); -ok( $dbh, "Connect with driver attributes in hash" ); +ok ($dbh, "Connect with driver attributes in hash"); -ok( $dbh->do(q/drop table if exists FRED/), 'drop table' ); +ok ($dbh->do ("drop table if exists FRED"), "drop table"); -my $dirfext = $^O eq 'VMS' ? '.sdbm_dir' : '.dir'; +my $dirfext = $^O eq "VMS" ? ".sdbm_dir" : ".dir"; -$dbh->do(q/create table fred (a integer, b integer)/); -ok( -f File::Spec->catfile( $dir, "FRED$dirfext" ), "FRED$dirfext exists" ); +$dbh->do ("create table fred (a integer, b integer)"); +ok (-f File::Spec->catfile ($dir, "FRED$dirfext"), "FRED$dirfext exists"); rmtree $dir; mkpath $dir; -if ($using_dbd_gofer) -{ +if ($using_dbd_gofer) { # can't modify attributes when connect through a Gofer instance - $dbh->disconnect(); - $dbh = DBI->connect( 'dbi:DBM:', undef, undef, { - f_dir => $dir, - sql_identifier_case => 2, # SQL_IC_LOWER - } - ); -} -else -{ - $dbh->dbm_clear_meta('fred'); # otherwise the col_names are still known! - $dbh->{sql_identifier_case} = 2; # SQL_IC_LOWER -} + $dbh->disconnect (); + $dbh = DBI->connect ("dbi:DBM:", undef, undef, { + f_dir => $dir, + sql_identifier_case => 2, # SQL_IC_LOWER + }); + } +else { + $dbh->dbm_clear_meta ("fred"); # otherwise the col_names are still known! + $dbh->{sql_identifier_case} = 2; # SQL_IC_LOWER + } -$dbh->do(q/create table FRED (a integer, b integer)/); -ok( -f File::Spec->catfile( $dir, "fred$dirfext" ), "fred$dirfext exists" ); +$dbh->do ("create table FRED (a integer, b integer)"); +ok (-f File::Spec->catfile ($dir, "fred$dirfext"), "fred$dirfext exists"); my $tblfext; -unless( $using_dbd_gofer ) -{ - $tblfext = $dbh->{dbm_tables}->{fred}->{f_ext} || ''; - $tblfext =~ s{/r$}{}; - ok( -f File::Spec->catfile( $dir, "fred$tblfext" ), "fred$tblfext exists" ); -} +unless ($using_dbd_gofer) { + $tblfext = $dbh->{dbm_tables}{fred}{f_ext} || ""; + $tblfext =~ s{/r$}{}; + ok (-f File::Spec->catfile ($dir, "fred$tblfext"), "fred$tblfext exists"); + } -ok( $dbh->do(q/insert into fRED (a,b) values(1,2)/), 'insert into mixed case table' ); +ok ($dbh->do ("insert into fRED (a, b) values (1, 2)"), + "insert into mixed case table"); # but change fRED to FRED and it works. -ok( $dbh->do(q/insert into FRED (a,b) values(2,1)/), 'insert into uppercase table' ); +ok ($dbh->do ("insert into FRED (a, b) values (2, 1)"), + "insert into uppercase table"); -unless ($using_dbd_gofer) -{ - my $fn_tbl2 = $dbh->{dbm_tables}->{fred}->{f_fqfn}; - $fn_tbl2 =~ s/fred(\.[^.]*)?$/freddy$1/; +unless ($using_dbd_gofer) { + my $fn_tbl2 = $dbh->{dbm_tables}{fred}{f_fqfn}; + $fn_tbl2 =~ s/fred(\.[^.]*)?$/freddy$1/; my @dbfiles = grep { -f $_ } ( - $dbh->{dbm_tables}->{fred}->{f_fqfn}, - $dbh->{dbm_tables}->{fred}->{f_fqln}, - $dbh->{dbm_tables}->{fred}->{f_fqbn} . ".dir" - ); - foreach my $fn (@dbfiles) - { + $dbh->{dbm_tables}{fred}{f_fqfn}, + $dbh->{dbm_tables}{fred}{f_fqln}, + $dbh->{dbm_tables}{fred}{f_fqbn} . ".dir" + ); + foreach my $fn (@dbfiles) { my $tgt_fn = $fn; $tgt_fn =~ s/fred(\.[^.]*)?$/freddy$1/; - File::Copy::copy( $fn, $tgt_fn ); + File::Copy::copy ($fn, $tgt_fn); + } + $dbh->{dbm_tables}{krueger}{file} = $fn_tbl2; + + my $r = $dbh->selectall_arrayref ("select * from Krueger"); + ok (@$r == 2, "rows found via cloned mixed case table"); + + ok ($dbh->do ("drop table if exists KRUeGEr"), "drop table"); } - $dbh->{dbm_tables}->{krueger}->{file} = $fn_tbl2; - my $r = $dbh->selectall_arrayref(q/select * from Krueger/); - ok( @$r == 2, 'rows found via cloned mixed case table' ); +my $r = $dbh->selectall_arrayref ("select * from Fred"); +ok (@$r == 2, "rows found via mixed case table"); - ok( $dbh->do(q/drop table if exists KRUeGEr/), 'drop table' ); -} - -my $r = $dbh->selectall_arrayref(q/select * from Fred/); -ok( @$r == 2, 'rows found via mixed case table' ); - -SKIP: -{ - DBD::DBM::Statement->isa("SQL::Statement") or skip("quoted identifiers aren't supported by DBI::SQL::Nano",1); - my $abs_tbl = File::Spec->catfile( $dir, 'fred' ); - # work around SQL::Statement bug - DBD::DBM::Statement->isa("SQL::Statement") and SQL::Statement->VERSION() lt "1.32" and $abs_tbl =~ s|\\|/|g; - $r = $dbh->selectall_arrayref( sprintf( q|select * from "%s"|, $abs_tbl ) ); - ok( @$r == 2, 'rows found via select via fully qualified path' ); -} - -if( $using_dbd_gofer ) -{ - ok( $dbh->do(q/drop table if exists FRED/), 'drop table' ); - ok( !-f File::Spec->catfile( $dir, "fred$dirfext" ), "fred$dirfext removed" ); -} -else -{ - my $tbl_info = { file => "fred$tblfext" }; - - ok( $dbh->disconnect(), "disconnect" ); - $dbh = DBI->connect( 'dbi:DBM:', undef, undef, { - f_dir => $dir, - sql_identifier_case => 2, # SQL_IC_LOWER - dbm_tables => { fred => $tbl_info }, - } - ); +SKIP: { + DBD::DBM::Statement->isa ("SQL::Statement") + or skip ("quoted identifiers aren't supported by DBI::SQL::Nano", 1); + my $abs_tbl = File::Spec->catfile ($dir, "fred"); + # work around SQL::Statement bug + DBD::DBM::Statement->isa ("SQL::Statement") + and SQL::Statement->VERSION () lt "1.32" + and $abs_tbl =~ s{\\}{/}g; + $r = $dbh->selectall_arrayref (sprintf 'select * from "%s"', $abs_tbl); + ok (@$r == 2, "rows found via select via fully qualified path"); + } + +if ($using_dbd_gofer) { + ok ($dbh->do ("drop table if exists FRED"), "drop table"); + ok (!-f File::Spec->catfile ($dir, "fred$dirfext"), "fred$dirfext removed"); + } +else { + my $tbl_info = {file => "fred$tblfext"}; + + ok ($dbh->disconnect (), "disconnect"); + $dbh = DBI->connect ("dbi:DBM:", undef, undef, { + f_dir => $dir, + sql_identifier_case => 2, # SQL_IC_LOWER + dbm_tables => {fred => $tbl_info}, + }); my @tbl; @tbl = $dbh->tables (undef, undef, undef, undef); - is( scalar @tbl, 1, "Found 1 tables"); + is (scalar @tbl, 1, "Found 1 tables"); - $r = $dbh->selectall_arrayref(q/select * from Fred/); - ok( @$r == 2, 'rows found after reconnect using "dbm_tables"' ); + $r = $dbh->selectall_arrayref ("select * from Fred"); + ok (@$r == 2, "rows found after reconnect using 'dbm_tables'"); - my $deep_dir = File::Spec->catdir( $dir, 'deep' ); + my $deep_dir = File::Spec->catdir ($dir, "deep"); mkpath $deep_dir; - $dbh = DBI->connect( 'dbi:DBM:', undef, undef, { - f_dir => $deep_dir, - sql_identifier_case => 2, # SQL_IC_LOWER - } - ); - ok( $dbh->do( q{create table wilma (a integer, b char (10))} ), "Create wilma" ); - ok( $dbh->do( q{insert into wilma values (1, 'Barney')} ), "insert Barney" ); - ok( $dbh->disconnect(), "disconnect" ); - - $dbh = DBI->connect( 'dbi:DBM:', undef, undef, { - f_dir => $dir, - sql_identifier_case => 2, # SQL_IC_LOWER - } - ); + $dbh = DBI->connect ("dbi:DBM:", undef, undef, { + f_dir => $deep_dir, + sql_identifier_case => 2, # SQL_IC_LOWER + }); + ok ($dbh->do ("create table wilma (a integer, b char (10))"), "Create wilma"); + ok ($dbh->do ("insert into wilma values (1, 'Barney')"), "insert Barney"); + ok ($dbh->disconnect (), "disconnect"); + + $dbh = DBI->connect ("dbi:DBM:", undef, undef, { + f_dir => $dir, + sql_identifier_case => 2, # SQL_IC_LOWER + }); # Make sure wilma is not found without f_dir_search @tbl = $dbh->tables (undef, undef, undef, undef); - is( scalar @tbl, 1, "Found 1 table"); - ok( $dbh->disconnect(), "disconnect" ); + is (scalar @tbl, 1, "Found 1 table"); + ok ($dbh->disconnect (), "disconnect"); - $dbh = DBI->connect( 'dbi:DBM:', undef, undef, { - f_dir => $dir, - f_dir_search => [ $deep_dir ], - sql_identifier_case => 2, # SQL_IC_LOWER - } - ); + $dbh = DBI->connect ("dbi:DBM:", undef, undef, { + f_dir => $dir, + f_dir_search => [ $deep_dir ], + sql_identifier_case => 2, # SQL_IC_LOWER + }); @tbl = $dbh->tables (undef, undef, undef, undef); - is( scalar @tbl, 2, "Found 2 tables"); + is (scalar @tbl, 2, "Found 2 tables"); # f_dir should always appear before f_dir_search - like( $tbl[0], qr{(?:^|\.)fred$}i, "Fred first" ); - like( $tbl[1], qr{(?:^|\.)wilma$}i, "Fred second" ); + like ($tbl[0], qr{(?:^|\.)fred$}i, "Fred first"); + like ($tbl[1], qr{(?:^|\.)wilma$}i, "Fred second"); - my( $n, $sth ); - ok( $sth = $dbh->prepare( 'select * from fred' ), "select from fred" ); - ok( $sth->execute, "execute fred" ); + my ($n, $sth); + ok ($sth = $dbh->prepare ("select * from fred"), "select from fred"); + ok ($sth->execute, "execute fred"); $n = 0; $n++ while $sth->fetch; - is( $n, 2, "2 entry in fred" ); - ok( $sth = $dbh->prepare( 'select * from wilma' ), "select from wilma" ); - ok( $sth->execute, "execute wilma" ); + is ($n, 2, "2 entry in fred"); + ok ($sth = $dbh->prepare ("select * from wilma"), "select from wilma"); + ok ($sth->execute, "execute wilma"); $n = 0; $n++ while $sth->fetch; - is( $n, 1, "1 entry in wilma" ); + is ($n, 1, "1 entry in wilma"); + + ok ($dbh->do (q/drop table if exists FRED/), "drop table fred"); + ok (!-f File::Spec->catfile ($dir, "fred$dirfext"), "fred$dirfext removed"); + ok (!-f File::Spec->catfile ($dir, "fred$tblfext"), "fred$tblfext removed"); + + ok ($dbh->do (q/drop table if exists wilma/), "drop table wilma"); + ok (!-f File::Spec->catfile ($deep_dir, "wilma$dirfext"), + "wilma$dirfext removed"); + ok (!-f File::Spec->catfile ($deep_dir, "wilma$tblfext"), + "wilma$tblfext removed"); + } - ok( $dbh->do(q/drop table if exists FRED/), 'drop table fred' ); - ok( !-f File::Spec->catfile( $dir, "fred$dirfext" ), "fred$dirfext removed" ); - ok( !-f File::Spec->catfile( $dir, "fred$tblfext" ), "fred$tblfext removed" ); - - ok( $dbh->do(q/drop table if exists wilma/), 'drop table wilma' ); - ok( !-f File::Spec->catfile( $deep_dir, "wilma$dirfext" ), "wilma$dirfext removed" ); - ok( !-f File::Spec->catfile( $deep_dir, "wilma$tblfext" ), "wilma$tblfext removed" ); -} +unless ($using_dbd_gofer) { + ok ($dbh = DBI->connect ("dbi:DBM:", undef, undef, { + f_dir => $dir, + f_dir_search => [ "t" ], + }), "New dbh for CVE"); + $dbh->{dbm_tables}{fred}{file} = + File::Spec->catdir (Cwd::abs_path ( + File::Spec->catdir ($dir, "..")), "fred"); + my @msg; + eval { + local $SIG{__DIE__} = sub { push @msg => @_ }; + local $dbh->{PrintError} = 0; + $dbh->do ("create table fred (a integer, b integer)"); + }; + like ("@msg", qr{is unsafe and not allowed}, "unsafe is caught"); + } -done_testing(); +done_testing (); diff -Nru libdbi-perl-1.647/t/60preparse.t libdbi-perl-1.652/t/60preparse.t --- libdbi-perl-1.647/t/60preparse.t 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/t/60preparse.t 2026-08-11 12:17:09.000000000 +0000 @@ -10,9 +10,6 @@ if ($DBI::PurePerl) { plan skip_all => 'preparse not supported for DBI::PurePerl'; } - else { - plan tests => 39; - } } my $dbh = DBI->connect("dbi:ExampleP:", "", "", { @@ -143,6 +140,21 @@ # --------------------------------------------------------------------- # +is( pp($dbh, 'a = :99999', DBIpp_ph_qm, DBIpp_ph_cs|DBIpp_ph_cn), undef, 'out of sequence'); +ok( $DBI::err ); +is( $DBI::errstr, "preparse found placeholder :99999 out of sequence, expected :1"); +is( pp($dbh, 'a = :100000', DBIpp_ph_qm, DBIpp_ph_cs|DBIpp_ph_cn), undef, 'exceeds limit'); +ok( $DBI::err ); +is( $DBI::errstr, "preparse found :p100000 which is outside the allowed range."); +is( pp($dbh, 'a = :2147483648', DBIpp_ph_qm, DBIpp_ph_cs|DBIpp_ph_cn), undef, 'exceeds limit'); +ok( $DBI::err ); +is( $DBI::errstr, "preparse found :p-2147483648 which is outside the allowed range."); +is( pp($dbh, 'a = :12345678987654321', DBIpp_ph_qm, DBIpp_ph_cs|DBIpp_ph_cn), undef, 'exceeds limit'); +ok( $DBI::err ); +like( $DBI::errstr, qr{^preparse found :p\d+ which is outside the allowed range.$}); + $dbh->disconnect; +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/65transact.t libdbi-perl-1.652/t/65transact.t --- libdbi-perl-1.647/t/65transact.t 2013-04-05 06:08:36.000000000 +0000 +++ libdbi-perl-1.652/t/65transact.t 2026-07-22 12:23:21.000000000 +0000 @@ -10,8 +10,6 @@ plan skip_all => 'Transactions not supported by DBD::Gofer' if $ENV{DBI_AUTOPROXY} && $ENV{DBI_AUTOPROXY} =~ /^dbi:Gofer/i; -plan tests => 10; - my $dbh = DBI->connect('dbi:ExampleP(AutoCommit=>1):', undef, undef) or die "Unable to connect to ExampleP driver: $DBI::errstr"; @@ -32,4 +30,6 @@ ok($dbh->{AutoCommit}); ok(!$dbh->{BegunWork}); +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/72childhandles.t libdbi-perl-1.652/t/72childhandles.t --- libdbi-perl-1.647/t/72childhandles.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/72childhandles.t 2026-07-22 12:23:32.000000000 +0000 @@ -23,8 +23,6 @@ exit 0; } -plan tests => 16; - my $using_dbd_gofer = ($ENV{DBI_AUTOPROXY}||'') =~ /^dbi:Gofer.*transport=/i; my $drh; @@ -146,4 +144,6 @@ is scalar @live, 0; } +done_testing; + 1; diff -Nru libdbi-perl-1.647/t/82sponge.t libdbi-perl-1.652/t/82sponge.t --- libdbi-perl-1.647/t/82sponge.t 1970-01-01 00:00:00.000000000 +0000 +++ libdbi-perl-1.652/t/82sponge.t 2026-05-28 11:36:38.000000000 +0000 @@ -0,0 +1,72 @@ +#! /usr/bin/env perl + +# vim: noet ts=2 sw=2: + +use strict; +use warnings; +use Test::More tests => 17; + +use Storable qw(dclone); +use DBI qw(:sql_types); + +# our reference table: +# +# A0 B1 C2 +# ------- --------- ------- +# foo NULL bazooka +# foolery bar NULL +# NULL barrowman baz +# + +# Historically, DBD::Sponge defaulted an sth's PRECISION to the length +# of its column names, meaning that some DBI shells could truncate row +# display. For example, formatting a row ('fo', NULL, 'ba') from our +# reference table above. + +our @NAMES = ( 'A0', 'B1', 'C2' ); +our @ROWS = (['foo', undef, 'bazooka'], + ['foolery', 'bar', undef ], + [undef, 'barrowman', 'baz' ]); + +my $dbh = DBI->connect("dbi:Sponge:", '', ''); +ok($dbh, "connect(dbi:Sponge:) succeeds"); + +my $sth = $dbh->prepare("simple, correct sponge", { + rows => dclone( \@ROWS ), + NAME => [ @NAMES ], + }); + +ok($sth, "prepare() of 3x3 result succeeded"); +is_deeply($sth->{NAME}, ['A0', 'B1', 'C2'], "column NAMEs as expected"); +is_deeply($sth->{TYPE}, [SQL_VARCHAR, SQL_VARCHAR, SQL_VARCHAR], + "column TYPEs default to SQL_VARCHAR"); +# +# Old versions of DBD-Sponge defaulted PRECISION (data "length") to +# length of the field _names_ rather than the length of the _data_. +# +is_deeply($sth->{PRECISION}, [7, 9, 7], + "column PRECISION matches lengths of longest field data"); +is_deeply($sth->fetch(), $ROWS[0], "first row fetch as expected"); +is_deeply($sth->fetch(), $ROWS[1], "second row fetch as expected"); +is_deeply($sth->fetch(), $ROWS[2], "third row fetch as expected"); +ok(!defined($sth->fetch()), "fourth fetch returns undef"); + +# Test that DBD-Sponge preserves bogus user-supplied attributes but +# ignores them when returning rows +$sth = $dbh->prepare('user-supplied silly TYPE and PRECISION', { + rows => dclone( \@ROWS ), + NAME => [qw( first_col second_col third_col )], + TYPE => [SQL_INTEGER, SQL_DATETIME, SQL_CHAR], + PRECISION => [1, 100_000, 0], + }); +ok($sth, "prepare() 3x3 result with TYPE and PRECISION succeeded"); +is_deeply($sth->{NAME}, ['first_col','second_col','third_col'], + "column NAMEs again as expected"); +is_deeply($sth->{TYPE}, [SQL_INTEGER, SQL_DATETIME, SQL_CHAR], + "column TYPEs not overwritten"); +is_deeply($sth->{PRECISION}, [1, 100_000, 0], + "column PRECISION not overwritten"); +is_deeply($sth->fetch(), $ROWS[0], "first row fetch as expected, despite bogus attributes"); +is_deeply($sth->fetch(), $ROWS[1], "second row fetch as expected, despite bogus attributes"); +is_deeply($sth->fetch(), $ROWS[2], "third row fetch as expected, despite bogus attributes"); +ok(!defined($sth->fetch()), "fourth fetch returns undef, despite bogus attributes"); diff -Nru libdbi-perl-1.647/t/90sql_type_cast.t libdbi-perl-1.652/t/90sql_type_cast.t --- libdbi-perl-1.647/t/90sql_type_cast.t 2025-01-17 09:49:51.000000000 +0000 +++ libdbi-perl-1.652/t/90sql_type_cast.t 2026-07-22 12:24:41.000000000 +0000 @@ -77,30 +77,12 @@ } } -if ($] >= 5.010001) { - # Some numeric tests fail the return value test on Perls before 5.10.1 - # because sv_2nv leaves NOK set - changed in 5.10.1 probably via the - # following change: - # The public IV and NV flags are now not set if the string - # value has trailing "garbage". This behaviour is consistent with not - # setting the public IV or NV flags if the value is out of range for the - # type. - push @tests, ( +push @tests, ( ['non numeric cast to double', "aabb", SQL_DOUBLE, 0, NO_CAST_NO_STRICT, q{["aabb"]}], ['non numeric cast to double (strict)', "aabb", SQL_DOUBLE, DBIstcf_STRICT, NO_CAST_STRICT, q{["aabb"]}] ); -} - -my $tests = @tests; -$tests *= 2 if $jx; -foreach (@tests) { - $tests++ if ($dp) && ($_->[3] & DBIstcf_DISCARD_STRING); - $tests++ if ($dp) && ($_->[2] == SQL_DOUBLE); -} - -plan tests => $tests; foreach my $test(@tests) { my $val = $test->[1]; @@ -154,4 +136,6 @@ } } +done_testing; + 1;