DBD-D1
view release on metacpan or search on metacpan
lib/DBD/D1.pm view on Meta::CPAN
package DBD::D1;
# DBD::D1 - DBI driver for Cloudflare D1 (SQLite-compatible serverless database)
# Communicates with Cloudflare D1 via the REST API using HTTP::Tiny and JSON::PP.
use strict;
use warnings;
our $VERSION = '0.02';
our $err = 0;
our $errstr = '';
our $sqlstate = '';
our $drh = undef;
use DBI ();
use DBI::DBD;
sub driver {
return $drh if $drh;
my ($class, $attr) = @_;
$class .= '::dr';
$drh = DBI::_new_drh($class, {
Name => 'D1',
Version => $VERSION,
Err => \$DBD::D1::err,
Errstr => \$DBD::D1::errstr,
State => \$DBD::D1::sqlstate,
Attribution => 'DBD::D1',
}) or return undef;
return $drh;
}
sub CLONE { undef $drh }
# ---------------------------------------------------------------
# Internal HTTP helper
# ---------------------------------------------------------------
package DBD::D1::_http;
use strict;
use warnings;
use HTTP::Tiny ();
use JSON::PP ();
my $json = JSON::PP->new->utf8->allow_nonref;
sub _ssl_available { HTTP::Tiny->can_ssl ? 1 : 0 }
# Returns ($result_arrayref, undef) on success or (undef, $error_string) on failure.
sub query {
my ($account_id, $database_id, $api_token, $sql, $params) = @_;
unless (_ssl_available()) {
return (undef,
'DBD::D1 requires HTTPS. Install IO::Socket::SSL and Net::SSLeay: '
. 'cpanm IO::Socket::SSL Net::SSLeay');
}
my $url = sprintf(
'https://api.cloudflare.com/client/v4/accounts/%s/d1/database/%s/query',
$account_id, $database_id,
);
my $body = $json->encode({
sql => $sql,
params => ($params && @$params) ? $params : [],
});
my $http = HTTP::Tiny->new(
default_headers => {
'Authorization' => "Bearer $api_token",
'Content-Type' => 'application/json',
},
timeout => 30,
);
my $res = $http->post($url, { content => $body });
# Try to parse JSON response to get detailed error info
my $data;
if ($res->{content}) {
$data = eval { $json->decode($res->{content}) };
}
unless ($res->{success}) {
my $detail = $res->{content} // '';
if ($res->{status} == 599 && $detail =~ /ssl|IO::Socket/i) {
return (undef,
'HTTPS failed (status 599). '
. 'Install IO::Socket::SSL and Net::SSLeay: '
. 'cpanm IO::Socket::SSL Net::SSLeay');
}
# If we have JSON error details, use those
if ($data && ref $data eq 'HASH') {
if (!$data->{success}) {
my $errs = $data->{errors} // [];
if (@$errs) {
my $msg = $errs->[0]{message} // 'Unknown D1 API error';
return (undef, $msg);
}
}
}
return (undef, sprintf('HTTP %s: %s', $res->{status}, $res->{reason} // 'Unknown'));
}
if (!$data) {
$data = eval { $json->decode($res->{content}) };
if ($@) { return (undef, "JSON decode error: $@") }
}
unless ($data->{success}) {
my $errs = $data->{errors} // [];
my $msg = @$errs ? $errs->[0]{message} : 'Unknown D1 API error';
return (undef, $msg);
}
return ($data->{result}, undef);
}
# ---------------------------------------------------------------
# DBD::D1::dr â driver handle
# ---------------------------------------------------------------
package DBD::D1::dr;
use strict;
use warnings;
$DBD::D1::dr::imp_data_size = 0;
# DSN: dbi:D1:account_id=<id>;database_id=<id>
# Pass Cloudflare API token as $password.
sub connect {
my ($drh, $dsn, $user, $auth, $attr) = @_;
my %dsnargs;
for my $pair (split /;/, $dsn) {
my ($k, $v) = split /=/, $pair, 2;
$dsnargs{$k} = $v if defined $k && defined $v;
}
# Use DBI->set_err on the drh with the caller's err/errstr so that
# PrintError/RaiseError on the caller handle control output, not the drh.
my $account_id = $dsnargs{account_id}
or return $drh->set_err(1,
"DBD::D1 connect: 'account_id' missing from DSN", undef, 'connect');
my $database_id = $dsnargs{database_id}
or return $drh->set_err(1,
"DBD::D1 connect: 'database_id' missing from DSN", undef, 'connect');
lib/DBD/D1.pm view on Meta::CPAN
=encoding UTF-8
=head1 NAME
DBD::D1 - DBI driver for Cloudflare D1 (serverless SQLite)
=head1 VERSION
0.02
=head1 SYNOPSIS
use DBI;
my $dbh = DBI->connect(
'dbi:D1:account_id=<ACCOUNT_ID>;database_id=<DATABASE_ID>',
undef,
$ENV{CF_API_TOKEN},
{ RaiseError => 1, PrintError => 0 },
) or die $DBI::errstr;
my $sth = $dbh->prepare('SELECT * FROM users WHERE active = ?');
$sth->execute(1);
while (my $row = $sth->fetchrow_hashref) {
printf "%s <%s>\n", $row->{name}, $row->{email};
}
$dbh->disconnect;
=head1 DESCRIPTION
B<DBD::D1> is a L<DBI> driver for L<Cloudflare D1|https://developers.cloudflare.com/d1/>,
Cloudflare's serverless SQLite-compatible relational database.
It communicates via the D1 REST API using L<HTTP::Tiny> and L<JSON::PP>
(both ship with Perl 5.14+), so no compiled extensions are required.
=head1 DSN FORMAT
dbi:D1:account_id=<ACCOUNT_ID>;database_id=<DATABASE_ID>
=head1 AUTHENTICATION
Pass your Cloudflare API token (B<D1 Edit> permission) as the C<$password>
argument to C<DBI-E<gt>connect()>.
=head1 LIMITATIONS
=over 4
=item * B<AutoCommit only> â D1 REST has no multi-statement transaction support.
=item * B<Column ordering> â rows arrive as JSON objects; column order follows
C<sort> on key names. Use C<fetchrow_hashref> for reliable named access.
=back
=head1 DEPENDENCIES
L<DBI>, L<HTTP::Tiny>, L<JSON::PP>, L<IO::Socket::SSL>, L<Net::SSLeay>
=head1 AUTHOR
Aldo Montes Zapata, C<< <amontes@cpan.org> >>
=head1 COPYRIGHT (c)
Copyright 2026 by Aldo Montes Zapata C<< <amontes@cpan.org> >>.
=head1 LICENSE
Same terms as Perl itself.
=cut
( run in 0.945 second using v1.01-cache-2.11-cpan-6aa56a78535 )