App-MBUtiny

 view release on metacpan or  search on metacpan

lib/App/MBUtiny/Collector/DBI.pm  view on Meta::CPAN

package App::MBUtiny::Collector::DBI; # $Id: DBI.pm 128 2019-07-06 15:27:48Z abalama $
use strict;
use utf8;

=encoding utf8

=head1 NAME

App::MBUtiny::Collector::DBI - Collector database interface

=head1 VERSION

Version 1.02

=head1 SYNOPSIS

    use App::MBUtiny::Collector::DBI;

    my $dbi = new App::MBUtiny::Collector::DBI(
        dsn => "DBI:mysql:database=mbutiny;host=mysql.example.com",
        user => "username",
        password => "password",
        set => [
            "RaiseError        0",
            "PrintError        0",
            "mysql_enable_utf8 1",
        ],
    );
    print STDERR $dbi->error if $dbi->error;

=head1 DESCRIPTION

Collector database interface

=head2 new

    my $dbi = new App::MBUtiny::Collector::DBI(
        dsn => "DBI:mysql:database=mbutiny;host=mysql.example.com",
        user => "username",
        password => "password",
        set => [
            "RaiseError        0",
            "PrintError        0",
            "mysql_enable_utf8 1",
        ],
    );

Creates DBI object

=head2 add

    $dbi->add(
        type => 0,
        name => "foo",
        addr => "127.0.0.1",
        status => 0,
        file => "foo-2019-06-25.tar.gz",
        size => 123456,
        md5 => "...",
        sha1 => "...",
        error => "...",
        comment => "...",
    ) or die $dbi->error;

Add new record on collector database

=head2 del

    $dbi->del(
        type => 0,
        name => "foo",
        addr => "127.0.0.1",
        file => "foo-2019-06-25.tar.gz",
    ) or die $dbi->error;

Delete record from collector database

=head2 dsn

    my $dsn = $dbi->dsn;

Returns DSN string of current collector database connection

=head2 error

    my $error = $dbi->error;
    $dbi->error("Error message");

Gets/sets error string

=head2 get

    my %info = $dbi->get(
        name => "foo",
        file => "foo-2019-06-25.tar.gz",
    );

Gets information about file from collector database

Format:

lib/App/MBUtiny/Collector/DBI.pm  view on Meta::CPAN

=head2 list

    my @files = $dbi->list(
        name => "foo"
    );

Returns list of files by specified the name

Record format of return result: see L</get>

=head2 report

    my @files = $dbi->report(
        start => 123456789
    );

Returns list of all last backup files, starting at the specified the "start" value

Record format of return result: see L</get>

=head1 SEE ALSO

L<App::MBUtiny>, L<CTK::DBI>

=head1 AUTHOR

Serż Minus (Sergey Lepenkov) L<http://www.serzik.com> E<lt>abalama@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 1998-2019 D&D Corporation. All Rights Reserved

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.

See C<LICENSE> file and L<https://dev.perl.org/licenses/>

=cut

use vars qw/$VERSION @EXPORT_OK/;
$VERSION = '1.02';

use Carp;
use CTK::DBI;
use CTK::Util qw/touch sharedstatedir/;
use CTK::ConfGenUtil;
use CTK::TFVals qw/ :ALL /;
use File::Spec;

use constant {
    PREFIX                  => 'mbutiny',
    COLLECTOR_DB_FILENAME   => 'mbutiny.db',
    DEFAULT_ADDR            => '127.0.0.1',
    REPORT_PERIOD           => 24*60*60 + 1, # Yesterday + 1sec
    DEFAULT_DSN_MASK        => 'dbi:SQLite:dbname=%s',
    DEFAULT_DBI_ATTR        => {
            dsn         => '', # See DEFAULT_DSN_MASK
            user        => '',
            password    => '',
            set         => [
                    'RaiseError 0',
                    'PrintError 0',
                    'sqlite_unicode 1',
                ],
        },
};

use constant COLLECTOR_DDL  => <<'DDL';
CREATE TABLE IF NOT EXISTS mbutiny (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    `type` INTEGER DEFAULT 0,         -- 0=internal/1=external
    `time` NUMERIC DEFAULT 0,         -- time()
    `name` CHAR(255) DEFAULT NULL,    -- name of mbutiny host
    `addr` CHAR(45) DEFAULT NULL,     -- client ip addr
    `status` INTEGER DEFAULT 0,       -- backup status
    `file` CHAR(255) DEFAULT NULL,    -- backup filename
    `size` INTEGER DEFAULT 0,         -- size of backup file
    `md5` CHAR(32) DEFAULT NULL,      -- md5-checksum of backup file
    `sha1` CHAR(40) DEFAULT NULL,     -- sha1-checksum of backup file
    `error` TEXT DEFAULT NULL,        -- error message
    `comment` TEXT DEFAULT NULL       -- comment
)
DDL

use constant COLLECTOR_INSERT  => <<'DML';
INSERT INTO mbutiny
    (`type`, `time`, `name`, `addr`, `status`, `file`, `size`, `md5`, `sha1`, `error`, `comment`)
VALUES
    (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
DML

use constant COLLECTOR_DELETE  => <<'DML';
DELETE FROM mbutiny WHERE `type` = ? AND `name` = ? AND `file` = ? AND `addr` = ?
DML

use constant COLLECTOR_SELECT  => <<'DML';
SELECT `id`, `type`, `time`, `name`, `addr`, `status`, `file`, `size`, `md5`, `sha1`, `error`, `comment`
FROM mbutiny
WHERE `name` = ?
DML

use constant COLLECTOR_SELECT_FILE  => <<'DML';
SELECT `id`, `type`, `time`, `name`, `addr`, `status`, `file`, `size`, `md5`, `sha1`, `error`, `comment`
FROM mbutiny
WHERE `name` = ? AND `file` = ?
ORDER BY `time` DESC
LIMIT 1
DML

use constant COLLECTOR_SELECT_LASTFILE  => <<'DML';
SELECT `id`, `type`, `time`, `name`, `addr`, `status`, `file`, `size`, `md5`, `sha1`, `error`, `comment`
FROM mbutiny
WHERE `name` = ?
ORDER BY `time` DESC
LIMIT 1
DML

use constant COLLECTOR_REPORT  => <<'DML';
SELECT `id`, `type`, `time`, `name`, `addr`, `status`, `file`, `size`, `md5`, `sha1`, `error`, `comment`
FROM mbutiny
WHERE `time` > ?
DML

use base qw/Exporter/;
@EXPORT_OK = qw/
        COLLECTOR_DB_FILENAME
        COLLECTOR_DB_FILE
    /;

sub COLLECTOR_DB_FILE { File::Spec->catfile(sharedstatedir(), PREFIX, COLLECTOR_DB_FILENAME) }

sub new {
    my $class = shift;
    my %args = @_;
    unless ($args{dsn}) {
        my $dda = DEFAULT_DBI_ATTR;
        foreach (%$dda) {
            $args{$_} //= $dda->{$_}
        }
    }
    my $file = $args{file} || COLLECTOR_DB_FILE();
    my $dsn = $args{dsn} || sprintf(DEFAULT_DSN_MASK, $file);

    # DB
    my $db = new CTK::DBI(
        -dsn    => $dsn,
        -debug  => 0,
        -username => $args{'user'},
        -password => $args{'password'},
        -attr     => _attr($args{'set'}),
        $args{timeout} ? (
            -timeout_connect => $args{timeout},
            -timeout_request => $args{timeout},
        ) : (),
        $args{user} ? () : (),
    );
    my $dbh = $db->connect if $db;

    # SQLite
    my $fnew = 0;
    my $issqlite = 0;
    if ($dbh && $dsn =~ /SQLite/i) {
        $file = $dbh->sqlite_db_filename();
        unless ($file && (-e $file) && !(-z $file)) {
            touch($file);
            chmod(0666, $file);
            $fnew = 1;
        }
        $issqlite = 1;
    }

    my $error = "";
    if (!$db) {
        $error = sprintf("Can't init database \"%s\"", $dsn);
    } elsif (!$dbh) {
        $error = sprintf("Can't connect to database \"%s\": %s", $dsn, $DBI::errstr || "unknown error");
    } elsif ($fnew) {
        $db->execute(COLLECTOR_DDL);
        $error = $dbh->errstr();
    }
    unless ($error) {
        $error = sprintf("Can't init database \"%s\". Ping failed: %s",
            $dsn, $dbh->errstr() || "unknown error") unless $dbh->ping;
    }

    my $self = bless {
            file    => $file,
            issqlite=> $issqlite,
            dsn     => $dsn,
            error   => $error,
            db      => $db,
        }, $class;

    return $self;
}
sub error {
    my $self = shift;
    my $err = shift;
    return $self->{error} unless defined $err;
    $self->{error} = $err;
    return $self->{error};
}
sub dsn {
    my $self = shift;
    return $self->{dsn};
}
sub is_sqlite {
    my $self = shift;
    return $self->{issqlite} ? 1 : 0;



( run in 0.902 second using v1.01-cache-2.11-cpan-39bf76dae61 )