Revision history for Database-Abstraction
0.37 Mon Jul 27 16:58:13 EDT 2026
[ Enhancements ]
- Remote file support: new host => 'server' (or 'user@host') constructor
parameter; when present, all candidate file extensions are fetched from
the remote machine via File::Slurp::Remote (SSH/SCP) into a local
File::Temp directory before the existing extension-based probe runs;
all backends (CSV, PSV, XML, SQLite, DBM::Deep) work transparently;
File::Temp::Dir object stored in _remote_tmpdir is auto-cleaned in
DESTROY; host name validated in new() against a strict identifier regex
(prevents shell injection); the local -d directory check is skipped
when host is given (directory is a remote path); File::Slurp::Remote
is loaded lazily (only when host is given)
- Local-host short-circuit: when host => 'localhost', '127.0.0.1', '::1',
or the current machine's hostname (short or FQDN) is given, _open()
reads the directory directly without loading or calling File::Slurp::Remote;
implemented by _is_local_host() which strips optional user@ prefix then
matches loopback literals and Sys::Hostname::hostname() (both short and
FQDN forms); new() restores the local -d directory check for local hosts;
host validation regex extended to allow ':' so IPv6 addresses (::1, etc.)
are accepted
- DBM::Deep backend: files with .dbm or .deep extension are detected
(after SQLite, before BerkeleyDB) and slurped into the in-memory hash
at open time via DBM::Deep->new(read_only=>1); all existing slurp
fast-paths (selectall_arrayref, selectall_array, fetchrow_hashref,
AUTOLOAD, columns, schema) work unchanged; type is set to 'Deep'
- DBM::Deep magic-byte detection: _is_deep_db() checks the first 4 bytes
for 'DPDB' (0x44 0x50 0x44 0x42, the standard DBM::Deep signature) or
'DPDP' (0x44 0x50 0x44 0x50); in _open() the .db candidate is probed
for Deep magic before the BerkeleyDB check, so .db files that are
actually DBM::Deep are correctly identified without requiring a .dbm
or .deep extension
- count() general in-memory scan: added a third fast-path branch that
fires when $self->{'data'} is set AND there is no DBI handle (i.e.
Deep backend); allows count(col => $val) without DBI; SQL-backed
backends (CSV/XML/SQLite) still fall through to SQL so column-name
validation continues to fire
- Query builder (Query.pm) all()/first()/count(): added Deep alongside
BerkeleyDB in the in-memory delegate path so that query()->where()->all()
etc. work on Deep files without a DBI handle
[ Security ]
- new() clone path: validate 'id' argument before bless in the clone
branch; previously the early return at line 540 bypassed the id
validation block at lines 558-562, allowing $obj->new(id => "x); DROP
TABLE t--") to produce an object whose $self->{'id'} was directly
interpolated (unbound) into ORDER BY, COUNT(), and CSV-comment-filter
SQL clauses; fix adds the same /^[a-zA-Z_][a-zA-Z0-9_]*$/ guard
inside the clone branch before bless
[ Bug fixes ]
- t/{csv,csv_id,schema,locales,no_entry,bugs}.t: replaced use_ok() with
compile-time use + pass() to prevent "Too late to run CHECK block"
warnings from Sub::Private (used by Log::Abstraction); use_ok() wraps
its argument in a runtime string eval, past the CHECK phase, which
caused Test::NoWarnings failures in those six test files
- Don't attempt to fixate empty data
- _fixate(): new private wrapper around Data::Reuse::fixate() that
suppresses the spurious "Use of uninitialized value in hash slice"
warning emitted by Data::Alias's XS hash-aliasing code on older Perl
versions when a source hashref contains undef column values (NULL
database fields); Data::Reuse still fixates correctly — the warning
is a false positive from Data::Alias not fully initialising key SVs
in the older internal Perl hash representation; all four fixate
call-sites in Abstraction.pm now go through _fixate(); confirmed
reproducible with Data::Alias 1.30 on Perl < 5.34
- t/bugs.t BUG10: regression guard for the above warning across all
three fixate code-paths (slurp, selectall_arrayref DBI,
selectall_array DBI)
- _fixate(): move Data::Reuse::forget() inside the wrapper so it covers
all four fixate call-sites including the slurp path; the slurp fixate
was previously exempt on the grounds that slurped hashrefs are long-lived
and don't create stale entries themselves, but it was still vulnerable to
stale entries left by earlier objects whose hashrefs were freed after going
out of scope — the memory allocator can reuse those addresses for new slurp
hashrefs, causing fixate() to alias them to wrong canonicals (root cause of
an intermittent t/unit.t failure where $db->number('one') occasionally
returned 2 instead of 1); the explicit Data::Reuse::forget() calls that
previously preceded _fixate() in selectall_arrayref and selectall_array
are now redundant and have been removed
0.36 Thu Jul 9 21:38:49 EDT 2026
[ Bug fixes ]
- Data::Reuse stale-address hazard: selectall_arrayref() and
selectall_array() now call Data::Reuse::forget() immediately before
fixate() to clear stale address→canonical mappings that could cause
one row's data to be silently substituted for another's on repeated
DBI calls (intermittent wrong-row returns in join queries)
- Windows-portable file detection: replaced File::pfopen (which splits
the path on ':' and breaks Windows drive letters) with
File::Spec->catfile + -r checks in _open_table()
- t/locales.t: trim_loc() regex now uses /s flag so it correctly strips
multi-line Carp::confess stack traces, not just single-line croak output
[ Security ]
- Input validation / SQL injection guards: id column name (new()),
dbname and filename (_open()), and join table name (_build_joins())
are now validated against strict identifier regexes; invalid values
croak with a descriptive message before any SQL is constructed
- _like_match(): replaced regex-based LIKE translation (%->.*) with an
O(m*n) dynamic-programming matcher in slurp mode, eliminating
catastrophic backtracking on patterns such as %a%a%a%a%b
[ Enhancements ]
- Prepared statement caching: all DBI prepare() calls replaced with
prepare_cached() for statement-handle reuse across queries
- Schema introspection: new columns() and schema() methods; SQLite uses
PRAGMA table_info, other DBI drivers use column_info, slurp mode
inspects the first row; results are cached after the first call
- DSN portability: new() now accepts dsn/username/password parameters,
bypassing file detection entirely; dialect is auto-detected from the
DSN prefix (sqlite/postgres/mysql); SQLite performance PRAGMAs are
only applied when connecting via a sqlite DSN
- Operator-hash query criteria: all select methods now accept hashref
values for comparison operators (> < >= <= != -like -not_like
-in -not_in -between) and logical groupings (-or -and); backed by
new private helpers _build_where / _build_where_conditions /
_match_criterion / _has_complex_criteria
- Automatic joins: selectall_arrayref(), selectall_array(), and
fetchrow_hashref() accept a join parameter (hashref or arrayref of
hashrefs with keys table/on/type); backed by _build_joins(); valid
JOIN types are INNER LEFT RIGHT FULL CROSS (default INNER)
- Chained query builder: new query() method returns a
Database::Abstraction::Query object supporting fluent method chaining:
->select()->where()->join()->order_by()->limit()->offset()->all()/
first()/count()
- Use Class::Abstract to ensure it's not instantiated directly
- DBD::SQLite::Constants and Data::Dumper are now loaded lazily (only
when the relevant code path is actually exercised)
0.35 Mon Jun 8 08:25:03 EDT 2026
[ Bug fixes ]
- Pass f_dir on open
- Canonicalize directory path via Cwd::abs_path to fix DBD::File safety check rejecting paths containing '..'
[ Enhancements ]
Updated to the latest test dashboard
0.34 Mon Jan 19 21:20:49 EST 2026
Fix logger with array test when a configuration file has been set up
Allow execute() to take an argument(s)
0.33 Sat Oct 18 18:35:46 EDT 2025
Using undef to searches sets IS NULL
Latest testing dashboard
0.32 Tue Sep 16 08:26:41 EDT 2025
Fix fast track return in selectall_arrayref
0.31 Mon Sep 15 20:15:00 EDT 2025
Call sth->finish() when needed
selectall_arrayref was setting an extra level of reference
Added the test dashboard
0.30 Sun Aug 24 15:11:14 EDT 2025
slurp mode - remove a data copy
selectall_hashref now returns undef if there are no matches
rewrote selectall_hashref to not just call selectall_hash, that saves data copying
Imply 'entry' argument when possible
Added the _fatal method
selectall_hashref name is misleading, will be selectall_arrayref
selectall_hash name is misleading, will be selectall_array
Fix potential memory leak issues
0.29 Mon Aug 18 21:42:25 EDT 2025
Moved the test data from data to t/data
Fixed wildcard usage determination
Optimise SQLite Pragmas
Removed data copy when slurping XML with no_entry set
Selectall_hashref - remove data copy
Reduce chance of cache key name clash
0.28 Wed Aug 6 19:46:59 EDT 2025
Added the import method
Fix determination of BerkeleyDB
Added BerkeleyDB test
Increase SQLite timeout
Added the count() method
AUTOLOAD - sanity check on the column name
0.27 Sat Jun 14 20:56:27 EDT 2025
Use Object::Configure rather than Class::Debug
0.26 Tue Apr 15 16:42:48 EDT 2025
Added the 'no_fixate' option
POC - fixate slurped data
0.25 Sun Apr 13 08:00:22 EDT 2025
Fix auto_load calling unknown routine isFalse
Removed _info and _notice as they're not used
More extensive use of Params::Get to clean the code
Added sanity test
Allow subclasses to set max_slurp_size on open()
Improved slurping of XML files - still needs more work
Use Data::Reuse, but note RT#100461
0.24 Wed Mar 26 11:46:57 EDT 2025
Added basic support for BerkeleyDBs (files ending in .db)
If config_file has a section for this class, use that
0.23 Tue Mar 25 08:06:33 EDT 2025
Not all loggers (e.g. Log::Any) can cope with a reference to an ARRAY
0.22 Fri Mar 14 08:02:39 EDT 2025
Allow the AUTOLOAD feature to be disabled
Added the auto_load parameter to disable AUTOLOAD
Force _open/_log to be a private/protected method
Use Params::Get and Log::Abstraction
Added config_file argument to new()
0.21 Tue Feb 11 07:45:15 EST 2025
Fix "Not an ARRAY reference"
0.20 Thu Feb 6 20:29:09 EST 2025
Fix the manifest
0.19 Thu Feb 6 20:19:07 EST 2025
Fix selectall_hashref() in slurp mode when no_entry is set
0.18 Thu Feb 6 07:26:02 EST 2025
Cache hits in list context returns a ref to the array, rather than the array itself
0.17 Tue Feb 4 15:32:43 EST 2025
Support expires_in for compatability with CHI
Added cache support to AUTOLOAD
Allow the logger to be a filename
When the logger is a reference to code, give more information
Added the 'filename' property
Don't attempt to slurp files when col_names is set
0.16 Sun Jan 12 12:15:01 EST 2025
fetchrow_hashref() - when no_entry is not set allow just one argument to be given: the entry value
0.15 Thu Jan 9 19:43:43 EST 2025
Improved handling of empty values in slurp mode
AUTOLOAD: error on invalid column name in slurp mode
0.14 Fri Jan 3 11:17:11 EST 2025
Added the dbname argument
0.13 Sun Dec 22 13:30:13 EST 2024
Refactor logger helper routines
Fix "Bad table or column name: 'entryIS NOT NULL' has chars not alphanumeric or underscore"
Added t/30-basics.t
0.12 Wed Dec 4 08:19:09 EST 2024
Move the synopsis document around for clarity
Allow the logger to be a ref to code
0.11 Thu Nov 7 10:44:24 EST 2024
Added id parameter
Slurp small XML files using XML::Simple
Fix uninitialised error when slurp_file doesn't exist
Use features of File::pfopen version 0.03
0.10 Thu Sep 19 08:17:55 EDT 2024
Added t/init.t
The constructor now honours 'sep_char'
Test no_entry files
Added CircleCI and Codecov
0.09 Mon Jun 24 20:42:48 EDT 2024
Added t/chi.t
selectall_hash: speed up slurp mode when 'entry' is one of the parameters
execute: add the table name to the query
0.08 Mon May 20 08:43:08 EDT 2024
Add unique as a synonym for distinct
Allow max_slurp_size to be tweaked
Fix CSV search on a column other than the keyed column
Added t/error.t
Catch undefined arguments in fetchrow_hashref
0.07 Fri Feb 23 08:02:24 EST 2024
selectall_hash: Fixed "Not an ARRAY reference"
0.06 Tue Feb 20 16:55:33 EST 2024
Slurped data - improve keying
fetchrow_hashref now uses slurped data on some optimised places
fetchrow_hashref: fixed uninitialized variable in logger
fetchrow_hashref: more verbose debugging on match
AUTOLOAD: usage error if entry isn't a column
AUTOLOAD: fix problem when no args are given on slurped data
0.05 Fri Feb 9 11:01:00 EST 2024
Fix https://www.cpantesters.org/cpan/report/f07e90f2-c264-11ee-bb2d-64236e8775ea
init() will now return the current defaults
Ensure that the default cache_duration is 1 hour
0.04 Fri Feb 2 13:14:18 EST 2024
init() can now be called more than once and will retain the old values
Added XML test
Restrict the size of the CSV file that are slurped to 16K
0.03 Thu Feb 1 13:48:01 EST 2024
Complain if the directory argument isn't a directory
Put the values from init() into a hash
Remove Database::Abstraction::Error - it's an artifact
It was a good idea, but Error doesn't play well with 'use lib'
0.02 Tue 30 Jan 11:07:12 EST 2024
Simpler package name used for testing
Fix the Error objection being thrown
0.01 Mon Jan 29 14:02:37 EST 2024
First draft