#=============================================================================
# Doloforge::Service::Postgres8 : add a PosgtreSQL 8 database for back-end use
#
# Generate config files from templates, add a config file to the whole, etc.
#
# Full help :
#   perldoc Doloforge::Service::Postgres8
#=============================================================================


package Doloforge::Service::Postgres8;

use strict;

# This base class implements the constructor and help methods in a generic way.
use base qw(Doloforge::Service::Postgres7);

use DBI;
use DBD::Pg;



#=============================================================================
=encoding utf8

=head1 NAME

Doloforge::Service::Postgres8 : Add a PosgtreSQL 8 database for back-end use

=head1 SYNOPSIS

    # Update the full configuration, running each client/project/service update
    doloforge config --update

=head1 DESCRIPTION

Todo

=cut


#=============================================================================
# Private method that checks the presence of a DB name, DB owner, etc.
sub _check_db {
    my $self = shift;

    my ($option, $client_conf, $project_conf, $dbname, $dbhost, $dbport) = @_;

    # Connect to the "template1" database, the always-available central point for other databases manipulation.
    # Also cache the created connection, for other projects's use in the same instance...
    # The user running this process (root) must have privileged access to the database server : check pg_hba.conf
    # "CREATE DATABASE cannot run inside a transaction block" : no transactions on this connection...
    $self->{dbh} ||= DBI->connect("DBI:Pg(RaiseError=>1,AutoCommit=>1):dbname=template1;host=$dbhost;port=$dbport")
        or $self->error("Postgres template1 database connection denied on '$dbhost:$dbport'.");
    my $dbh = $self->{dbh};

    # Check for $dbname existance in the pg_database : the query returns the number of databases with the required name (0 or 1, obviously)
    unless ($self->_get_count($dbh, 'SELECT count(datname) AS count FROM pg_database WHERE datname = ?', $dbname)) {

        # We do not use "prepare_cached" below, because database and user names are not strings, but identifiers in the DB.
        # They can't be replaced by "bind values" with "?" in the SQL statements. They must be hard-coded.
        # Proper encoding of the identifers is still necessary ($dbh->quote_identifier will add "" quoting around the identifier, and will properly quote inner characters)

        # If the user (and group) already exist (for previous project's need), do not create them again :
        unless ($self->_get_count($dbh, 'SELECT count(groname) AS count FROM pg_group WHERE groname = ?', "$client_conf->{name}_adm")) {
            $self->message("Creating $self->{name} group '$client_conf->{name}_adm'");
            $dbh->do('CREATE ROLE ' . $dbh->quote_identifier("$client_conf->{name}_adm"));
        }
        unless ($self->_get_count($dbh, 'SELECT count(usename) AS count FROM pg_user WHERE usename = ?', $client_conf->{name})) {
            $self->message("Creating $self->{name} user '$client_conf->{name}'");
            $dbh->do('CREATE ROLE ' . $dbh->quote_identifier($client_conf->{name}) . ' LOGIN IN GROUP ' . $dbh->quote_identifier("$client_conf->{name}_adm"));
        }
        # Actually create the database :
        $self->message("Creating $self->{name} database '$dbname' on '$dbhost:$dbport'");
        $dbh->do('CREATE DATABASE ' . $dbh->quote_identifier($dbname) . ' WITH OWNER ' . $dbh->quote_identifier("$client_conf->{name}_adm") . ' ENCODING \'UNICODE\'');

        if ($project_conf->{description}) {
            # Add "COMMENT ON DATABASE" in the project database itself...
            # Reuse the $dbh variable itself, but still don't lose the connection to the template1 DB, cached in $self->{dbh}
            # No transaction, since there is only one statement...
            $dbh = DBI->connect("DBI:Pg(RaiseError=>1,AutoCommit=>1):dbname=$dbname;host=$dbhost;port=$dbport")
                or $self->error("Postgres $dbname database connection denied on '$dbhost:$dbport'.");
            # The $dbname string is a non-bindable DB identifier, thus the static SQL and the quote_identifier call...
            $dbh->do('COMMENT ON DATABASE ' . $dbh->quote_identifier($dbname) . ' IS ' . $dbh->quote($project_conf->{description}));
        }
    }
}



1;
__END__

=head1 AUTHOR

Nicolas Huillard <F<nicolas@huillard.net>>, Dolomède

=head1 SEE ALSO

doloforge(1)

=cut
