Data-TagDB
view release on metacpan or search on metacpan
lib/Data/TagDB.pm view on Meta::CPAN
# Copyright (c) 2024-2025 Philipp Schafft
# licensed under Artistic License 2.0 (see LICENSE file)
# ABSTRACT: Work with Tag databases
package Data::TagDB;
use v5.10;
use strict;
use warnings;
use Scalar::Util qw(weaken blessed);
use Carp;
use DBI;
use Data::TagDB::Tag;
use Data::TagDB::Relation;
use Data::TagDB::Metadata;
use Data::TagDB::LinkIterator;
use Data::TagDB::MultiIterator;
use Data::TagDB::WellKnown;
use Data::TagDB::Cloudlet;
use Data::URIID::Colour;
our $VERSION = v0.12;
my %_queries = (
_default => {
tag_by_hint => 'SELECT tag FROM hint WHERE name = ?',
_tag_simple_identifier => 'SELECT data FROM metadata WHERE relation = (SELECT tag FROM hint WHERE name = \'also-shares-identifier\') AND type = (SELECT tag FROM hint WHERE name = ?) AND context = 0 AND encoding = 0 AND tag = ? ORDER BY data D...
_tag_by_dbid_type_and_data => 'SELECT tag FROM metadata WHERE relation = (SELECT tag FROM hint WHERE name = \'also-shares-identifier\') AND type = ? AND context = 0 AND encoding = 0 AND data = ?',
_create_tag => 'INSERT INTO tag DEFAULT VALUES',
_create_metadata => 'INSERT OR IGNORE INTO metadata (tag,relation,context,type,encoding,data) VALUES (?,?,?,?,?,?)',
_create_relation => 'INSERT OR IGNORE INTO relation (tag,relation,related,context,filter) VALUES (?,?,?,?,?)',
},
Pg => {
_create_tag => 'INSERT INTO tag DEFAULT VALUES RETURNING id',
_create_metadata => 'INSERT INTO metadata (tag,relation,context,type,encoding,data) VALUES (?,?,?,?,?,?) ON CONFLICT DO NOTHING',
_create_relation => 'INSERT INTO relation (tag,relation,related,context,filter) VALUES (?,?,?,?,?) ON CONFLICT DO NOTHING',
},
);
sub new {
my ($pkg, $first, @rest) = @_;
my $DBI_name;
my $dbh;
my %query;
croak 'No dsn or dbh given to new' unless defined $first;
if (scalar(@rest) == 0 && eval { $first->can('prepare'); }) {
$dbh = $first;
} else {
$dbh = DBI->connect($first, @rest) or croak 'Cannot connect to database';
}
$DBI_name = $dbh->{Driver}{Name};
foreach my $name (keys %{$_queries{_default}}) {
$query{$name} = $dbh->prepare($_queries{$DBI_name}{$name} // $_queries{_default}{$name});
}
return bless {
dbh => $dbh,
_DBI_name => $DBI_name,
cache_tag => {},
cache_ise => {},
cache_default_type => {},
cache_default_encoding => {},
backup_type => {},
lib/Data/TagDB.pm view on Meta::CPAN
unless (defined($self->{transaction_type})) {
$self->{transaction_type} = $type;
$self->{transaction_open} = 0;
$self->dbh->begin_work;
}
if ($self->{transaction_type} eq $type || $self->{transaction_type} eq 'rw') {
# no-op
} elsif ($self->{transaction_type} eq 'ro' && $type eq 'rw') {
$self->{transaction_type} = $type;
} else {
$error = 'Transaction type missmatch';
}
unless (defined $error) {
$self->{transaction_open}++;
eval { $code->() };
$self->{transaction_open}--;
}
unless ($self->{transaction_open}) {
delete $self->{transaction_type};
$self->dbh->commit;
}
croak $error if defined $error;
}
# ---- Virtual methods ----
# ---- Private helpers ----
sub DESTROY {
my ($self) = @_;
eval { $self->disconnect };
}
sub assert_connected {
my ($self) = @_;
my $dbh = $self->{dbh};
confess 'Not connected to any database' unless defined $dbh;
return $dbh;
}
sub tag_by_dbid {
my ($self, $dbid) = @_;
my $cache = $self->{cache_tag};
if (defined $cache->{$dbid}) {
return $cache->{$dbid};
} else {
state $done = 0;
my $tag = Data::TagDB::Tag->_new(db => $self, dbid => $dbid);
if ($done++ > 1024) {
$self->_cache_maintain;
$done = 0;
}
$cache->{$dbid} = $tag;
weaken($cache->{$dbid});
return $tag;
}
}
sub _tag_by_ise_cached {
my ($self, $ise, $autocreate) = @_;
if (defined $self->{cache_ise}{$ise}) {
return $self->tag_by_dbid($self->{cache_ise}{$ise});
} else {
my $tag = $self->tag_by_id(uuid => $ise, $autocreate); # TODO: Allow all ISE here.
$self->{cache_ise}{$ise} = $tag->dbid;
return $tag;
}
}
sub _cache_maintain {
my ($self) = @_;
my $cache = $self->{cache_tag};
foreach my $key (keys %{$cache}) {
delete $cache->{$key} unless defined $cache->{$key};
}
}
sub _cache_clear {
my ($self) = @_;
$self->_cache_maintain;
%{$self->{cache_ise}} = ();
}
sub _as_tag {
my ($self, $id, $autocreate) = @_;
return undef unless defined $id;
return $id if eval {$id->isa('Data::TagDB::Tag')};
return $self->tag_by_id(Data::Identifier->new(from => $id, db => $self), $autocreate);
}
sub _default_type {
my ($self, $relation) = @_;
my $relation_dbid = $relation->dbid;
if (defined $self->{cache_default_type}{$relation_dbid}) {
return $self->tag_by_dbid($self->{cache_default_type}{$relation_dbid});
} else {
my $type = eval {$self->relation(tag => $relation, relation => $self->wk->default_type)->one->related};
if (defined $type) {
$self->{cache_default_type}{$relation_dbid} = $type->dbid;
} elsif (defined $self->{backup_type}{$relation_dbid}) {
return $self->tag_by_dbid($self->{cache_default_type}{$relation_dbid} = $self->{backup_type}{$relation_dbid});
} else {
die 'No default type known';
}
return $type;
}
}
sub _default_encoding {
my ($self, $type) = @_;
my $type_dbid = $type->dbid;
if (defined $self->{cache_default_encoding}{$type_dbid}) {
( run in 1.449 second using v1.01-cache-2.11-cpan-39bf76dae61 )