package PgCommitFest::DB; require DBI; use strict; use warnings; sub connect { my ($class, $datasource, $username, $password) = @_; return bless { 'dbh' => DBI->connect($datasource, $username, $password, { 'AutoCommit' => 0, 'RaiseError' => 1 }), 'trace' => 0, 'dirty' => 0, }, $class; } sub commit { my ($self) = @_; warn "COMMIT" if $self->{'trace'}; $self->{'dirty'} = 0; return $self->{'dbh'}->commit(); } sub delete { my ($self, $table, $criteria) = @_; my (@where, @bind); while (my ($k, $v) = each %$criteria) { if (ref $v) { push @where, $self->{'dbh'}->quote_identifier($k) . ' = ' . $$v; } else { push @where, $self->{'dbh'}->quote_identifier($k) . ' = ?'; push @bind, $v; } } my $sql = 'DELETE FROM ' . $self->{'dbh'}->quote_identifier($table) . (@where ? ' WHERE ' . join(' AND ', @where) : ''); warn $sql if $self->{'trace'}; $self->{'dirty'} = 1; return $self->{'dbh'}->do($sql, {}, @bind); } sub do { my ($self, $sql, @bind) = @_; warn $sql if $self->{'trace'}; $self->{'dirty'} = 1; return $self->{'dbh'}->do($sql, {}, @bind); } sub disconnect { my ($self) = @_; return $self->{'dbh'}->disconnect(); } sub insert { my ($self, $table, $data) = @_; my (@values, @bind); for my $v (values %$data) { if (ref $v) { push @values, $$v; } else { push @values, '?'; push @bind, $v; } } my $sql = 'INSERT INTO ' . $self->{'dbh'}->quote_identifier($table) . ' (' . join(', ', keys %$data) . ') VALUES (' . join(', ', @values) . ')'; warn $sql if $self->{'trace'}; $self->{'dirty'} = 1; return $self->{'dbh'}->do($sql, {}, @bind); } sub insert_returning_id { my ($self, $table, $data) = @_; my (@values, @bind); for my $v (values %$data) { if (ref $v) { push @values, $$v; } else { push @values, '?'; push @bind, $v; } } my $sql = 'INSERT INTO ' . $self->{'dbh'}->quote_identifier($table) . ' (' . join(', ', keys %$data) . ') VALUES (' . join(', ', @values) . ') RETURNING id'; warn $sql if $self->{'trace'}; $self->{'dirty'} = 1; my $sth = $self->{'dbh'}->prepare($sql); $sth->execute(@bind); my $result = $sth->fetchrow_hashref; return $result->{'id'}; } sub quote { my ($self, $value) = @_; return $self->{'dbh'}->quote($value); } sub rollback { my ($self) = @_; $self->{'dirty'} = 0; return $self->{'dbh'}->commit(); } sub select { my ($self, $sql, @bind) = @_; warn $sql if $self->{'trace'}; return $self->{'dbh'}->selectall_arrayref($sql, { 'Slice' => {} }, @bind); } sub select_one { my ($self, $sql, @bind) = @_; warn $sql if $self->{'trace'}; my $sth = $self->{'dbh'}->prepare($sql); $sth->execute(@bind); my $result = $sth->fetchrow_hashref; return $result; } sub tidy { my ($self) = @_; $self->{'dbh'}->rollback() if $self->{'dirty'}; return undef; } sub update { my ($self, $table, $criteria, $data) = @_; my (@values, @where, @bind); while (my ($k, $v) = each %$data) { if (ref $v) { push @values, $self->{'dbh'}->quote_identifier($k) . ' = ' . $$v; } else { push @values, $self->{'dbh'}->quote_identifier($k) . ' = ?'; push @bind, $v; } } while (my ($k, $v) = each %$criteria) { if (ref $v) { push @where , $self->{'dbh'}->quote_identifier($k) . ' = ' . $$v; } else { push @where, $self->{'dbh'}->quote_identifier($k) . ' = ?'; push @bind, $v; } } return if ! @values; # Nothing to do? my $sql = 'UPDATE ' . $self->{'dbh'}->quote_identifier($table) . ' SET ' . join(', ', @values) . (@where ? ' WHERE ' . join(' AND ', @where) : ''); warn $sql if $self->{'trace'}; $self->{'dirty'} = 1; return $self->{'dbh'}->do($sql, {}, @bind); } 1;