NAME
Punk::Model::DBI - the default DBI backend for Punk models
DESCRIPTION
The shipped Punk::Model backend: plain DBI, no ORM. Connections are pooled by dsn and shared across every model that uses them, so a hundred models on one database open one handle per worker, not a hundred; a $$ check reconnects after a fork. RaiseError and AutoCommit are on, and generated SQL is prepared through prepare_cached so each distinct statement is compiled once.
It is selected by default; database backend => 'Class' swaps it for any class honouring the same six methods. Every call blocks the worker for the whole database round trip - Punk::Model::DBIx::Loop is the non-blocking alternative, at the cost of handlers written against futures.
CONFIGURATION
From the database keyword:
database
dsn => 'dbi:SQLite:dbname=myapp.db',
user => $user, # optional
password => $pass, # optional
attr => { ... }; # optional, merged into the connect attrs
CONSTRUCTOR
new
Punk::Model::DBI->new(database => \%conn, table => $t,
primary => $pk, columns => \@names);
Built by "_instantiate" in Punk::Model from the database options and the model's table, primary key and columns. Not called directly.
dbh
The live per-worker DBI handle for this backend's dsn, connected on first use and shared with every other backend on the same database.
THE CONTRACT
get(%key)
SELECT * ... WHERE key = ? ... - the row hashref, or undef.
search(\%filter, \%opts)
The filter with its operators ("The filter" in Punk::Model), ORDER BY the order_by columns with the primary key as the tie-breaker ("Ordering" in Punk::Model), LIMIT one past limit (default 20). Returns
{ rows => [ \%row, ... ], has_more_data => 0|1, next => $token|undef }
next is a url-safe, opaque encoding of the last row's values under the ordering. Passed back as after => $token it continues from that row - under a single column a WHERE pk ?>, under several the expanded comparison (a < ?) OR (a = ? AND b ?) OR ...> with each column in its own direction - so pagination is seek-based, not offset-based, and a mixed ordering pages correctly. Identifiers are quoted through the connection and values bound; keys are sorted so one filter shape is one prepared statement.
count(\%filter)
SELECT COUNT(*) ... WHERE ... for the same filter; the number.
all()
search({}, {}).
transactions
$c->txn on this backend is begin_work, the block, commit - or rollback and a rethrow when the block dies or the commit fails. There is one connection per database per worker, so every model on that database is inside the transaction for the length of the block, whether it came through $tx->model or $c->model, and so is a raw statement on $tx->handle, which is the $dbh. A second txn inside the block croaks: nested transactions are not supported, and a silent join would stop a rollback covering what the outer block thought it covered.
create(\%data)
Inserts the known columns and returns the stored row (via RETURNING where the driver supports it - SQLite 3.35+ or PostgreSQL, detected once per connection - otherwise re-fetched by primary key).
update(\%key_and_changes)
Updates the row named by the primary key with the remaining columns; returns the stored row.
delete(%key)
Deletes and returns the affected row count.
SEE ALSO
Punk::Model, Punk, DBI.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)