#!/usr/bin/perl

use strict;
use warnings;
use DBI();
use OAR::IO;
use OAR::Conf qw(init_conf dump_conf get_conf is_conf set_value);
use File::Basename;
use Getopt::Long;
use Sort::Versions;
use Term::UI;
use Term::ReadLine;

Getopt::Long::Configure ("gnu_getopt");

#
# Default values used in this script.
#
my $DEFAULT_CONFFILE = "%%OARCONFDIR%%/oar.conf";
my $DEFAULT_MYSQL_PORT = 3306;
my $DEFAULT_PGSQL_PORT = 5432;
my $DEFAULT_DB_NAME = "oar";
my $DEFAULT_DB_HOST = "localhost";
my $DEFAULT_DB_USER = "oar";
my $DEFAULT_DB_RO_USER = "oar_ro";

my $DEFAULT_MYSQL_STRUCTURE="%%OARDIR%%/database/mysql_structure.sql";
my $DEFAULT_MYSQL_ADMISSION_RULES="%%OARDIR%%/database/mysql_default_admission_rules.sql";
my $DEFAULT_MYSQL_DEFAULT_DATA="%%OARDIR%%/database/default_data.sql";
my $DEFAULT_MYSQL_RESET="%%OARDIR%%/database/mysql_reset_structure.sql";

my $DEFAULT_PGSQL_STRUCTURE="%%OARDIR%%/database/pg_structure.sql";
my $DEFAULT_PGSQL_ADMISSION_RULES="%%OARDIR%%/database/pg_default_admission_rules.sql";
my $DEFAULT_PGSQL_DEFAULT_DATA="%%OARDIR%%/database/default_data.sql";
my $DEFAULT_PGSQL_RESET="%%OARDIR%%/database/pg_reset_structure.sql";

my %DEFAULT_PGSQL_UPGRADES = ();
my $DEFAULT_PGSQL_LAST_VERSION;

my %DEFAULT_MYSQL_UPGRADES = ();
my $DEFAULT_MYSQL_LAST_VERSION;

#
# meta functions/parameters defined during the execution (depending on the db type)
#
my $template_cmd = "cat";
my $exec_sql;
my $exec_sql_file;
my %DEFAULT_UPGRADES;
my $DEFAULT_LAST_VERSION;
my $DEFAULT_STRUCTURE;
my $DEFAULT_ADMISSION_RULES;
my $DEFAULT_DATA;
my $DEFAULT_RESET;

#
# Options from the command line
#
my $usage;
my $debug;
my $force_sql;
my $force_yes;
my $db_type;
my $db_host;
my $db_port;
my $db_name;
my $db_user;
my $db_pass;
my $db_ro_user;
my $db_ro_pass;
my $db_admin_user;
my $db_admin_pass;
my $db_is_local;
my $action;
my $conffile;

#
# database handler
#
my $dbh;

#
# Gather the sql file information for upgrading the database
#
open LIST, "find %%OARDIR%%/database -name '*_structure_upgrade_*-*.sql' |" or die "Unable to list the SQL file for structure upgrades";
while (<LIST>) {
    my ($line) = $_;
    chomp($line);
    if ($line =~ /.*\/(mysql|pg)_structure_upgrade_([\d\.]+)-([\d\.]+)\.sql$/) {
        my ($type, $version_from, $version_to) = ($1,$2,$3);
        if ($type eq "mysql") {
            $DEFAULT_MYSQL_UPGRADES{$version_from} = [$version_to,$line];
            # process the last existing version
            if ($DEFAULT_MYSQL_LAST_VERSION) {
                if (versioncmp($version_to,$DEFAULT_MYSQL_LAST_VERSION) > 0) {
                    $DEFAULT_MYSQL_LAST_VERSION = $version_to;
                }

            } else {
                $DEFAULT_MYSQL_LAST_VERSION = $version_to;
            }
        } elsif ($type eq "pg") {
            $DEFAULT_PGSQL_UPGRADES{$version_from} = [$version_to,$line];
            # process the last existing version
            if ($DEFAULT_PGSQL_LAST_VERSION) {
                if (versioncmp($version_to,$DEFAULT_PGSQL_LAST_VERSION) > 0) {
                    $DEFAULT_PGSQL_LAST_VERSION = $version_to;
                }

            } else {
                $DEFAULT_PGSQL_LAST_VERSION = $version_to;
            }
        }
    }
}


#
# Display usage
#
sub print_usage() {
    print <<EOS;
Usage: $0 <ACTION> [OPTIONS]

Setup OAR database.

Actions:
     --create                       Create OAR database
     --drop                         Drop OAR database
     --upgrade                      Upgrade OAR database
     --reset                        Reset OAR database
     --check                        Check if database is ready

Options:
     --conf=OAR_CONF_FILE           Use a different OAR configuration file. Default is '%%OARCONFDIR%%/oar.conf'
     --db-type=DB_TYPE              Set the database type ('mysql' or 'Pg')
     --db-host=DB_HOST              Set the database hostname
     --db-port=DB_PORT              Set the database port
     --db-user=DB_USER              Set the database user
     --db-pass=DB_PASS              Set the database password
     --db-name=DB_NAME              Set the database name
     --db-ro-user=DB_RO_USER        Set the read-only database user 
     --db-ro-pass=DB_RO_PASS        Set the read-only database pass 

     --db-is-local                  The database is local, the script can use the local admin account to execute command (PostgreSQL) or the unix socket (MySQL)
     --db-admin-user=DB_ADMIN_USER  Set the database admin user to create the database. Only used with the --create action
     --db-admin-pass=DB_ADMIN_PASS  Set the database admin password to create the database. Only used with the --create action
 -y, --force-yes                    Force oar-database to continue without prompt even if it could be harmful
 -f, --force-sql                    Force the execution even if a SQL command fails
 -d, --debug                        Print debug information
 -h, --help                         Show this message

For more information, see the manual page.
EOS
}

sub get_current_schema_version() {
    my $query;

    if ($db_type eq "Pg") {
        $query="select version from schema limit 1;";
    } else {
        $query="select version from `schema` limit 1;";
    }
     
    my $sth = $dbh->prepare($query);
    $sth->execute();
    my $ver = $sth->fetchrow_hashref;

    unless ($ver) { return ""; }
    return $ver->{'version'};
}


sub init_database() {
    check_user_db_access($db_user,$db_pass);
    print "Initializing database '$db_name'...\n";
    &$exec_sql_file($DEFAULT_STRUCTURE) == 0       or die "Failed to execute $DEFAULT_STRUCTURE.\n";
    &$exec_sql_file($DEFAULT_DATA) == 0            or die "Failed to execute $DEFAULT_DATA.\n";
    &$exec_sql_file($DEFAULT_ADMISSION_RULES) == 0 or die "Failed to execute $DEFAULT_ADMISSION_RULES.\n";
}

sub reset_database() {
    print "Resetting database '$db_name'...\n";
    &$exec_sql_file($DEFAULT_RESET) == 0  or die "Failed to execute $DEFAULT_RESET\n.";
    init_database();
}

sub upgrade_database() {
    check_user_db_access($db_user,$db_pass);
    # Retrieve the current version (check if shema table exist before)
    my $current_version = get_current_schema_version();

    if (!$current_version) {
        print "No schema version found. Initialising the database.\n";
        init_database();
    } elsif ($current_version eq $DEFAULT_LAST_VERSION) {
        print "The database is up to date (version $current_version).\n";
    } else {
        print "The current schema version is $current_version. Upgrading to $DEFAULT_LAST_VERSION.\n"; 
        # DB_USER / DB_PASS are useless in upgrade files
        print "Apply templates:\n  DB_NAME=$db_name\n  DB_RO_USER=$db_ro_user\n  DB_RO_PASS=$db_ro_pass\n";
        $template_cmd = "perl -pe 's/%%DB_NAME%%/$db_name/g;s/%%DB_RO_USER%%/$db_ro_user/g;s/%%DB_RO_PASS%%/$db_ro_pass/g;'";
        while($current_version ne $DEFAULT_LAST_VERSION) {
            if ($DEFAULT_UPGRADES{$current_version}) {
                my ($to_version, $to_file) = @{$DEFAULT_UPGRADES{$current_version}};
                print "Uprgading from '$current_version' to '$to_version'...\n";
	    	    &$exec_sql_file($to_file) == 0 or die "Failed to upgrade the database from $current_version to $to_file with $to_file\n.";
            } else {
               	die "Unable to upgrade from the version '$current_version'\n";
            }
            $current_version = get_current_schema_version();
        }
    }
}

sub mysql_ro_user_privileges() {
    # All except the challenge table (localhost)
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.schema TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.accounting TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.admission_rules TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.assigned_resources TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_log_hostnames TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_logs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.files TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.frag_jobs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions_visu TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources_visu TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_dependencies TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_descriptions TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_groups TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_state_logs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_types TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.jobs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.moldable_job_descriptions TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.queues TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.resource_logs TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.resources TO '$db_ro_user'".'@'."'localhost' IDENTIFIED BY '$db_ro_pass'") ;
    # All except the challenge table (other hosts)
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.schema TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.accounting TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.admission_rules TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.assigned_resources TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_log_hostnames TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.event_logs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.files TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.frag_jobs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_predictions_visu TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.gantt_jobs_resources_visu TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_dependencies TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_descriptions TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_resource_groups TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_state_logs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.job_types TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.jobs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.moldable_job_descriptions TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.queues TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.resource_logs TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.resources TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
    mysql_admin_exec_sql("GRANT SELECT ON $db_name.walltime_change TO '$db_ro_user'".'@'."'%' IDENTIFIED BY '$db_ro_pass'") ;
}

sub fix_ro_user_privileges() {
    if ($db_type eq "mysql") {
    # This revokes the wildcard select privilege that was made by previous
    # versions of this script and set up the correct privileges for the ro user
    # (the bug affected only mysql databases)
      print "Fixing the ro user privileges in MySQL...\n";
      mysql_admin_exec_sql("REVOKE SELECT ON $db_name.* FROM '$db_ro_user'".'@'."'localhost'") ;
      mysql_admin_exec_sql("REVOKE SELECT ON $db_name.* FROM '$db_ro_user'".'@'."'%'") ;
      mysql_ro_user_privileges();
    } elsif ($db_type eq "Pg") {
    # This revokes the create privilege which is given to anyone by default: oar_ro once connected could create tables.
    # See: https://www.postgresql.org/docs/8.3/static/ddl-schemas.html#DDL-SCHEMAS-PRIV
      print "Fixing the ro user privileges in PostgreSQL...\n";
      pgsql_admin_exec_sql("REVOKE CREATE ON SCHEMA public FROM PUBLIC", $db_name);
      pgsql_admin_exec_sql("GRANT CREATE ON SCHEMA public TO $db_user", $db_name);
    }
}

sub create_database() {

    if ($db_type eq "mysql") {
        print "Creating the database...\n";
        mysql_admin_exec_sql("CREATE DATABASE $db_name CHARACTER SET latin1") ;

        print "Creating the database user...\n";
        mysql_admin_exec_sql("GRANT ALL PRIVILEGES ON $db_name.* TO '$db_user'" . '@' . "'localhost' IDENTIFIED BY '$db_pass'") ;
        mysql_admin_exec_sql("GRANT ALL PRIVILEGES ON $db_name.* TO '$db_user'" . '@' ."'%' IDENTIFIED BY '$db_pass'");
    } elsif ($db_type eq "Pg") {
        print "Creating the database user...\n";
        pgsql_admin_exec_sql("CREATE ROLE $db_user LOGIN PASSWORD '$db_pass';");
        if ($db_ro_user and $db_ro_pass) {
            print "Creating the database read-only user...\n";
            pgsql_admin_exec_sql("CREATE ROLE $db_ro_user LOGIN PASSWORD '$db_ro_pass';");
        }
        print "Creating the database...\n";
        pgsql_admin_exec_sql("CREATE DATABASE $db_name OWNER $db_user");
        pgsql_admin_exec_sql("REVOKE CREATE ON SCHEMA public FROM PUBLIC", $db_name);
        pgsql_admin_exec_sql("GRANT CREATE ON SCHEMA public TO $db_user", $db_name);
        pgsql_admin_exec_sql("GRANT ALL PRIVILEGES ON DATABASE $db_name TO $db_user");
    }


    init_database();

    if ($db_ro_user and $db_ro_pass) {
        if ($db_type eq "Pg")  {
            pgsql_admin_exec_sql("GRANT SELECT ON schema,
                                                  accounting,
                                                  admission_rules,
                                                  assigned_resources,
                                                  event_log_hostnames,
                                                  event_logs,
                                                  files,
                                                  frag_jobs,
                                                  gantt_jobs_predictions,
                                                  gantt_jobs_predictions_visu,
                                                  gantt_jobs_resources,
                                                  gantt_jobs_resources_visu,
                                                  job_dependencies,
                                                  job_resource_descriptions,
                                                  job_resource_groups,
                                                  job_state_logs,
                                                  job_types,
                                                  jobs,
                                                  moldable_job_descriptions,
                                                  queues,
                                                  resource_logs,
                                                  resources,
                                                  walltime_change,
                                                  admission_rules_id_seq,
                                                  event_logs_event_id_seq,
                                                  files_file_id_seq,
                                                  job_resource_groups_res_group_id_seq,
                                                  job_state_logs_job_state_log_id_seq,
                                                  job_types_job_type_id_seq,
                                                  moldable_job_descriptions_moldable_id_seq,
                                                  resource_logs_resource_log_id_seq,
                                                  resources_resource_id_seq,
                                                  jobs_job_id_seq
                                  TO $db_ro_user", $db_name);
        } elsif ($db_type eq "mysql") {
            print "Creating the database read-only user...\n";
            mysql_ro_user_privileges();
        }
    }
}

sub drop_database() {
    print "Removing the database...\n";
    if ($db_type eq "mysql") {
        mysql_admin_exec_sql("DROP DATABASE $db_name");
        mysql_admin_exec_sql("DROP USER $db_user". '@' . "'%'");
        mysql_admin_exec_sql("DROP USER $db_user" . '@localhost');
        if ($db_ro_user) {
            mysql_admin_exec_sql("DROP USER $db_ro_user" .'@'. "'%'");
            mysql_admin_exec_sql("DROP USER $db_ro_user" . '@localhost');
        }
    } elsif ($db_type eq "Pg") {
        pgsql_admin_exec_sql("DROP DATABASE $db_name");
            pgsql_admin_exec_sql("DROP USER $db_user");
        if ($db_ro_user) {
            pgsql_admin_exec_sql("DROP USER $db_ro_user");
        }
    }
}

sub mysql_exec_sql_file {
    my ($file) = @_;
    if ($debug) { 
        print "executing '$file'\n";
    }
    if ($force_sql) {
        return system("$template_cmd $file | mysql --force --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name");
    } else {
        return system("$template_cmd $file | mysql --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name");
    }
}

sub pgsql_exec_sql_file {
    my ($file) = @_;
    if ($debug) { 
        print "executing '$file'\n";
    }
    return system("$template_cmd $file | PGUSER='$db_user' PGPASSWORD='$db_pass' PGHOST='$db_host' PGPORT='$db_port' psql -q $db_name");
    
}

sub mysql_exec_sql {
    my ($query) = @_;
    if ($debug) {
        print "executing '$query'\n";
    }
    if ($force_sql) {
        return system("mysql --force --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name -e \"$query\"");
    } else {
        return system("mysql --user='$db_user' --password='$db_pass' --host='$db_host' --port='$db_port' $db_name -e \"$query\"");
    }
}

sub mysql_admin_exec_sql {
    my ($query) = @_;
    if ($debug) {
        print "executing '$query'\n";
    }
    if ($db_is_local) {
        return system("mysql -e \"$query\"");
    } else {
        return system("mysql --user='$db_admin_user' --password='$db_admin_pass' --host='$db_host' --port='$db_port' -e \"$query\"");
    }
}
sub pgsql_exec_sql {
    my ($query) = @_;
    if ($debug) {
        print "executing '$query'\n";
    }
    return system("PGUSER='$db_user' PGPASSWORD='$db_pass' PGHOST='$db_host' PGPORT='$db_port' psql -q $db_name -c \"$query\"");

}

sub pgsql_admin_exec_sql {
    my ($query,$db) = @_;
    unless ($db) {
        $db="";
    }
    if ($debug) {
        print "executing '$query'\n";
    }
    if ($db_is_local) {
        return system("echo \"$query\" | su - postgres -c \"psql $db\"");
    } else { 
        return system("PGUSER='$db_admin_user' PGPASSWORD='$db_admin_pass' PGHOST='$db_host' PGPORT='$db_port' psql $db -c \"$query\"");
    }
}


GetOptions ("help|h"                => \$usage,
            "debug|d"               => \$debug,
            "force-sql|f"           => \$force_sql,
            "force-yes|y"           => \$force_yes,
            "upgrade|s"             => sub { $action = 'upgrade' },
            "setup|s"               => sub { $action = 'upgrade' }, #  keep "--setup" for backward compatibility
            "reset|r"               => sub { $action = 'reset' },
            "create|c"              => sub { $action = 'create' },
            "drop"                  => sub { $action = 'drop' },
            "check"                 => sub { $action = 'check' },
            "fix-ro-user-priv"      => sub { $action = 'fix-ro-user-priv' },
            "conf=s"                => \$conffile,
#            "update-conf"           => sub { $action = 'update-conf' },
            "db-type=s"             => \$db_type,
            "db-host=s"             => \$db_host,
            "db-port=i"             => \$db_port,
            "db-user=s"             => \$db_user,
            "db-pass=s"             => \$db_pass,
            "db-ro-user=s"          => \$db_ro_user,
            "db-ro-pass=s"          => \$db_ro_pass,
            "db-name=s"             => \$db_name,
            "db-admin-user=s"       => \$db_admin_user,
            "db-admin-pass=s"       => \$db_admin_pass,
            "db-is-local"           => \$db_is_local
);


#
# Usage
#
if ($usage){ print_usage(); exit(0); }


#
# Check the action
#
if ( !$action ) {
    print "Error: You must specify the action do be done.\n\n";
    print_usage();
    exit(1);
}


#
# Check if the given conffile.
#
if ( ! $conffile ) { 
    $conffile=$DEFAULT_CONFFILE; 

}
unless ( -r $conffile ) {
    print "Error: The configuration file is not readable.\n\n";
    print_usage();
    exit(1);
}


#
# Set the parameters to use
#
OAR::Conf::init_conf($conffile);

if (! $db_type) {
    if (OAR::Conf::is_conf("DB_TYPE")) {
        $db_type = OAR::Conf::get_conf("DB_TYPE");

        if (OAR::Conf::is_conf("DB_PORT")) {
            $db_port = OAR::Conf::get_conf("DB_PORT");
        }
    } else {
        print "Error: The database type is not set.\n\n";
        print_usage();
        exit(1);
    }
} elsif ( $db_type ne "Pg" && $db_type ne "mysql") {
    print "Error: The database type must be either 'Pg' or 'mysql'.\n\n";
    print_usage();
    exit(1);
}

if (! $db_port ) {
    if ($db_type eq "mysql") { $db_port = $DEFAULT_MYSQL_PORT; }
    if ($db_type eq "Pg") { $db_port = $DEFAULT_PGSQL_PORT; }
}

unless ($db_host) {
    $db_host = (OAR::Conf::is_conf("DB_HOSTNAME") ? OAR::Conf::get_conf("DB_HOSTNAME") : $DEFAULT_DB_HOST);
}

unless ($db_user) {
    $db_user = (OAR::Conf::is_conf("DB_BASE_LOGIN") ? OAR::Conf::get_conf("DB_BASE_LOGIN") : $DEFAULT_DB_USER);
}
unless ($db_pass) {
    $db_pass = (OAR::Conf::is_conf("DB_BASE_PASSWD") ? OAR::Conf::get_conf("DB_BASE_PASSWD") : "");

    if (($db_pass eq "") or ($db_pass eq '""')) {
        print "Error: DB_BASE_PASSWD is not set in the config file.\n";
        exit(1);
    }
}

unless ($db_ro_user) {
    if (OAR::Conf::is_conf("DB_BASE_LOGIN_RO")) {
        $db_ro_user = OAR::Conf::get_conf("DB_BASE_LOGIN_RO");
    }
}

unless ($db_ro_pass) {
    if (OAR::Conf::is_conf("DB_BASE_PASSWD_RO")) {
        $db_ro_pass = OAR::Conf::get_conf("DB_BASE_PASSWD_RO");
    }
    if (($db_ro_pass eq "") or ($db_ro_pass eq '""')) {
        print "Error: DB_BASE_PASSWD_RO is not set in the config file.\n";
        exit(1);
    }
}

unless ($db_name) {
    $db_name = (OAR::Conf::is_conf("DB_BASE_NAME") ? OAR::Conf::get_conf("DB_BASE_NAME") : $DEFAULT_DB_NAME);
}

if ($action eq "create" or $action eq "drop" or $action eq "fix-ro-user-priv") {
    unless ($db_is_local) {
        unless ($db_admin_user) {
            print "Error: Please provide the database administrator credentials (with --db-admin-user and --db-admin-pass) or use --db-is-local.\n";
            exit(1);
        }
        unless ($db_admin_pass) {
            print "Warning: The admin password is empty. Proceeding with an empty password.\n";
            $db_admin_pass="";
        }
    }
}



#
# Set the meta function depending on the db type
#
if ($db_type eq "mysql") {
    $exec_sql                = \&mysql_exec_sql;
    $exec_sql_file           = \&mysql_exec_sql_file;
    $DEFAULT_LAST_VERSION    = $DEFAULT_MYSQL_LAST_VERSION;
    %DEFAULT_UPGRADES        = %DEFAULT_MYSQL_UPGRADES;
    $DEFAULT_RESET           = $DEFAULT_MYSQL_RESET;
    $DEFAULT_STRUCTURE       = $DEFAULT_MYSQL_STRUCTURE;
    $DEFAULT_ADMISSION_RULES = $DEFAULT_MYSQL_ADMISSION_RULES;
    $DEFAULT_DATA            = $DEFAULT_MYSQL_DEFAULT_DATA;
} elsif ($db_type eq "Pg") {
    $exec_sql                = \&pgsql_exec_sql;
    $exec_sql_file           = \&pgsql_exec_sql_file;
    $DEFAULT_LAST_VERSION    = $DEFAULT_PGSQL_LAST_VERSION;
    %DEFAULT_UPGRADES        = %DEFAULT_PGSQL_UPGRADES;
    $DEFAULT_RESET           = $DEFAULT_PGSQL_RESET;
    $DEFAULT_STRUCTURE       = $DEFAULT_PGSQL_STRUCTURE;
    $DEFAULT_ADMISSION_RULES = $DEFAULT_PGSQL_ADMISSION_RULES;
    $DEFAULT_DATA            = $DEFAULT_PGSQL_DEFAULT_DATA;
} else {
    die "You shouldn't execute this part of the code. Contact the developper\n";
}


#
# try a connection to the database
#
sub check_user_db_access {
    my ($user,$pass) = @_;

    my $dbc = "DBI:$db_type:database=$db_name;host=$db_host;port=$db_port";
    $dbh = DBI->connect($dbc, $user,$pass, {'InactiveDestroy' => 1, 'PrintError' => 1});
    if (!defined($dbh)) {
        print "Error: The connection to the database failed (user=$user,host=$db_host,port=$db_port,database=$db_name). Please check if the database server is up and ready.\n";
        print "       Please read %%DOCDIR%%/README.database for instructions.\n";
        exit(1);
    }
}

#
# update oar.conf if asked
#
if ($action eq "update-conf") {
    print "Updating $conffile with the given parameters.\n";
    OAR::Conf::set_value("DB_HOSTNAME",$db_host);
    OAR::Conf::set_value("DB_PORT",$db_port);
    OAR::Conf::set_value("DB_BASE_NAME",$db_name);
    OAR::Conf::set_value("DB_BASE_LOGIN",$db_user);
    OAR::Conf::set_value("DB_BASE_PASSWD",$db_pass);
    OAR::Conf::set_value("DB_BASE_LOGIN_RO",$db_ro_user);
    OAR::Conf::set_value("DB_BASE_PASSWD_RO",$db_ro_pass);
    OAR::Conf::set_value("DB_TYPE",$db_type);
}


#
# setup the database
#
my $term = Term::ReadLine->new("brand");
$Term::UI::VERBOSE = 0;

if ($action eq "check") {
    $ENV{'OARCONFFILE'} = "%%OARCONFDIR%%/oar.conf";
    if (not defined(OAR::IO::connect_ro_one_log("log"))) {
        print "[Failed] Cannot connect to OAR database.\n";
        exit 1;
    } else {
        check_user_db_access($db_user,$db_pass);
        # Retrieve the current version (check if shema table exist before)
        my $db_version = get_current_schema_version();
        if (not defined($db_version)) {
            print "[Failed] No OAR database schema version found.\n";
            exit 1;
        }
        if ($db_version ne $DEFAULT_LAST_VERSION) {
            print "[Failed] OAR database version mismatch. Current version is $db_version while $DEFAULT_LAST_VERSION is required.\n";
            exit 1;
        }
    }
    print "[Success] The OAR database is accessible and ready.\n";
    exit 0;
} elsif ($action eq "setup" or $action eq "upgrade") {
    check_user_db_access($db_user,$db_pass);
    print "WARNING: Please make sure to stop oar-server service before upgrading.\n";
    print "         Please consider backing up the database before upgrading.\n";
    if($force_yes || $term->ask_yn(
            prompt => "Continue?", 
            default => 'n')) {
        upgrade_database();
    } else {
        print "Aborted.\n.";
    }
} elsif ($action eq "reset") {
    check_user_db_access($db_user,$db_pass);
    print "WARNING: job history, resources, custom admission rules,... will be lost.\n";
    if ($force_yes || $term->ask_yn(
            prompt => "Continue?", 
            default => 'n')) {
        reset_database();
    } else {
        print "Aborted.\n.";
    }
} elsif ($action eq "create") {
    create_database();
} elsif ($action eq "drop") {
    print "WARNING: job history, resources, custom admission rules,... will be lost.\n";
    if ($force_yes || $term->ask_yn(
            prompt => "Continue?", 
            default => 'n')) {
        drop_database();
    } else {
        print "Aborted.\n."
    }
} elsif ($action eq "fix-ro-user-priv") {
    fix_ro_user_privileges();
}
