# $Id: DB.pm,v 1.11 2004/06/11 17:54:04 gigio Exp $
######################################
# Comas - Conference Management System
######################################
# Copyright 2003 CONSOL
# Congreso Nacional de Software Libre (http://www.consol.org.mx/)
#   Gunnar Wolf <gwolf@gwolf.cx>
#   Manuel Rabade <mig@mig-29.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
######################################

######################################
# Module: Comas::DB
# Handles the database connectivity for a Comas module
######################################
# Depends on:
#
# DBI - Database independent interface for Perl
# DBD::Pg - PostgreSQL database server driver for DBI
package Comas::DB;

use strict;
use warnings;
use DBI;
use Carp;
use Comas::Conf qw(:read :write);

=head1 NAME

Comas::DB - Handles the database connectivity for a Comas module

=head1 SYNOPSIS

$db = Comas::DB->new(-dbname => 'comas',
                     -dbuser => 'comas',
                     -dbpasswd => 'p4ssw0rd',
                     -host => undef,
                     -port => 5432);

All the attributes for the object creation are optional; C<-dbname>, C<-dbuser>,
C<-host> and C<-port> are shown with their default values. Password will 
default to undef unless supplied. 

You might get scared on why C<-host> is set to undef - It is so because 
localhost means making a TCP/IP connection to 127.0.0.1, while undef means
using a local connection - and this is handled differently in PostgreSQL's
configuration.

Comas::DB provides the following DBI methods:

    $sth = $db->prepare($sql);
    $ok = $db->begin_work;
    $ok = $db->commit;
    $ok = $db->rollback;

For all other DBI methods, they can be accessed through
C<$db-E<gt>{-dbh}-E<gt>methodname>, althought their use is discouraged.

Additionally, the following Comas-specific shortcuts are also provided:

To query a Comas::DB object on what was the last message produced by the
database:

    $text = $db->lastMsg();

Generic catalogs (tables with only 'id' and 'descr' as their attributes) are
used throughout Comas. Those catalogs are typically very small - You can get 
the whole catalog, or just the description for a given ID, by using:

    %catalog = $db->getCatalog($cat_name);
    $descr = $db->getCatDescr($cat_name, $id);

Where $cat_name is the name of the catalog (i.e., 'country' or 'track').

=head1 REQUIRES

    Comas::Conf - Comas configuration management routines
    DBI - Database independent interface for Perl
    DBD::Pg - PostgreSQL database server driver for DBI

=head1 SEE ALSO

L<DBI|DBI> - Database independent interface for Perl

L<Comas::Conf|Comas::Conf> - Comas configuration management routines

=head1 AUTHOR

Gunnar Wolf, gwolf@gwolf.cx

Manuel Rabade, mig@mig-29.net

Comas has been developed for CONSOL, Congreso Nacional de Software Libre,
http://www.consol.org.mx/

=head1 COPYRIGHT

Copyright 2003 Gunnar Wolf and Manuel Rabade

This library is free software, you can redistribute it and/or modify it
under the terms of the GPL version 2 or later.

=cut

#################################################################
# Object constructor and destructor

sub new {
    # The constructor for Comas::DB
    my ($db, $class, %par, %allowed);
    $class = shift;
    if (@_ % 2) {
	carp 'Invocation error - Wrong number of parameters';
	return undef;
    }
    %par = @_;

    # Initialize $db with default values
    $db = {-dbname => 'comas',
	   -dbuser => 'comas',
	   -host => undef,
	   -port => undef,
	   -lastMsg => ''};

    # Handle user-supplied parameters, checking no illegal parameters
    # were received
    %allowed = (-dbname => 1, -dbuser => 1, -host => 1, -port => 1,
		-dbpasswd => 1);

    for my $key (keys %par) {
	if (!defined $allowed{$key}) {
	    carp "Invalid parameter received ($key)";
	    return undef;
	}
	$db->{$key} = $par{$key};
    }

    # Open database connection
    my %dbparam = (AutoCommit => 1, PrintError => 1, RaiseError => 0,
		   ShowErrorStatement => 1);

    # Functional magic: Of those defined from dbname, host, port
    # place them in a string separated by ;
    my $dbiline = join ';' ,
      map { "$_=$db->{-$_}" }
        grep { defined $db->{-$_} }
          qw(dbname host port);

    $db->{-dbh} = DBI->connect("dbi:Pg:$dbiline",
                               $db->{-dbuser},
                               $db->{-dbpasswd},
                               \%dbparam) or
	carp("Failed to open database connection:\n",$DBI::errstr) &&
	return undef;

    # Ok, the object is created - Bless it and return
    bless $db, $class;
    return $db;
}

sub DESTROY {
    # In this destructor we explicitly disconnect from the database in order
    # to prevent warnings/inconsistencies from the system.
    my $db = shift;
    if (ref $db->{-dbh}) {
	# Rollback any pending transactions, if any, before disconnecting.
        unless ($db->{-dbh}->{AutoCommit}) {
            $db->{-dbh}->rollback;
        }
	$db->{-dbh}->disconnect;
    }
}

#################################################################
# Envelopes for functions passed to DBI

sub prepare {
    my ($db, $ret);
    $db = shift;
    return $db->{-dbh}->prepare(@_);
}

sub begin_work {
    my ($db, $ret);
    $db = shift;
    return $db->{-dbh}->begin_work(@_);
}

sub commit {
    my ($db, $ret);
    $db = shift;
    return  $db->{-dbh}->commit(@_);
}

sub rollback {
    my ($db, $ret);
    $db = shift;
    return $db->{-dbh}->rollback(@_);
}

#################################################################
# Informative functions

sub lastMsg {
    my $db = shift;
    my $ret = $db->{-dbh}->errstr;
    $ret =~ s/^ERROR:  //;
    return $ret;
}

#################################################################

sub getCatalog {
    my ($db, $cat, $sth, %ret);
    $db = shift;
    $cat = shift;
    %ret = ();

    unless ($sth = $db->prepare("SELECT id, descr FROM $cat") and 
	    $sth->execute) {
	carp "Unable to query for information on catalog $cat";
	return undef;
    }

    while (my @row = $sth->fetchrow_array) {
	$ret{$row[0]} = $row[1];
    }

    return %ret;
}

sub getCatDescr {
    my ($db, $cat, $id, $sth);
    $db = shift;
    $cat = shift;
    $id = shift;

    unless ($sth = $db->prepare("SELECT descr FROM $cat WHERE id = ?") and
	    $sth->execute($id)) {
	carp "Unable to query for item $id on catalog $cat";
	return undef;
    }

    return $sth->fetchrow_array;
}

1;

# $Log: DB.pm,v $
# Revision 1.11  2004/06/11 17:54:04  gigio
# Make the connection honor the 'host' argument. Original patch by
# izone.
#
# Revision 1.10  2004/04/07 21:32:41  gwolf
# Created Comas::DB methods getCatalog and getCatDescr, modified some of
# Comas::DB modules in order to use this interface instead of
# Comas::HTML::common::_id2descr (and removed that function, of course)
#
# Revision 1.9  2003/12/20 04:14:51  mig
# - Agrego tags Id y Log que expanda el CVS
#
