#!/usr/bin/perl
# $Id: db_update 28331 2013-04-11 16:58:49Z atsybulnik $
#
#  usage db_update [options] <confdb|transdb>
#
# Location:
# $APL/db/sql/confdb
# $APL/db/sql/transdb
# 
# Naming convention: <Major>.<Minor>.<Patch>-<Iteration>-<module>.sql
#
# Sort: Numeric order will be applied to the prefix part and alphabetic 
#       sort order will be applied to the remaining script's file name portion

use strict;
use Getopt::Long;
use Data::Dumper;
use SKM::DB qw(DBLocal);

# CONS ------------
my $APL=$ENV{APL} || die "APL is not defined\n";
my $LOGPATH="$ENV{APL_VAR}/log/update/db";
my $SQLPATH="$APL/db/sql";
my $PSQL="$APL/db/bin/db_exec";
my $DB_USR=$ENV{APL_DB_USR};

# VARS ------------
my $dbl;

# ARGS ------------
my ($fver,$fiter); # Force version
GetOptions (
  'force-version=s'    => \$fver,
  'force-iteration=s'  => \$fiter,
);
die "Incorrect version value" if defined $fver and $fver!~/^(\d+\.\d+\.\d+)$/;
die "Incorrect iteration value" if defined $fiter and $fiter!~/^\d+$/;;
my $db=$ARGV[0];

# FUNC ------------

sub write2log {
   my $log=shift;
   if(open LOG, $log) {
     print LOG @_;
     close LOG;
   }
}

sub Log {
   write2log(">>$LOGPATH/db_update.log",@_);
} 

sub Fatal {
  Log('FATAL ERROR:',@_);
  exit 1;
}

sub Error {
  Log('ERROR:',@_);
}

sub db_connect {
   $dbl=DBLocal({PrintError=>0,'RaiseError' => 1});
   $dbl->{FetchHashKeyName} = 'NAME_uc';
}

sub load_version {
 my $db=shift;
 my $ret;
 eval {               # table may not exists yet
   $ret=$dbl->selectall_arrayref("select version,iteration from version_$db",{Slice => {}});
 };
 if ($@) {
   eval {
     $ret=$dbl->selectall_arrayref("select version,iteration from public.version_$db",{Slice => {}});
   };
 }
 my %ver=(VERSION=>'2.7.0', ITERATION=>0); # default if empty
 if(defined $ret and @$ret) {
    %ver=%{$ret->[0]};
 }
 $ver{VERSION}=$fver if defined $fver;
 $ver{ITERATION}=$fiter if defined $fiter;
 $ver{VERSION}=~/^(\d+)\.(\d+)\.(\d+)$/ || Fatal("Mark in version_${db} is not correct\n");
 %ver=(%ver,MAJOR=>$1,MINOR=>$2,PATCH=>$3);
 return %ver;
}

sub save_version {
 my ($db,$mark)=@_;
 return if not $mark;    # skip emty marks
 Fatal("incorrect mark : $mark\n") if not $mark=~/^(\d+\.\d+\.\d+)-(\d+)$/;
 my ($version,$iteration)=($1,$2);
 eval {                  # table may not exists
   $dbl->do("delete from version_$db");
   $dbl->do("insert into version_$db(VERSION,ITERATION) values('$version',$iteration)");
   Log "saved $mark\n";
 };
}

sub apply {
  my ($db,$sql)=@_;
  my $log="$LOGPATH/$db-$sql.log";
  my $cmd="$PSQL -U $DB_USR -a -f $SQLPATH/$db/$sql skm_local 2>&1 | tee -a $log | egrep '^psql:.+ERROR:|No such file'";
  write2log(">$log","====COMMAND:====\n$cmd\n\n====OUTPUT:====\n\n");
  Log("APPLYING: $sql ");
  my $errors=`$cmd`;
  Fatal("\n$errors") if $errors;
  Log "\t\t\tSUCCESS\n";
}

# MAIN ==================================
  if( not defined $db or ! -d "$SQLPATH/$db" ) {
     die "\n\n Usage:\n\t\tdb_update [--force-version=ver] [--force-iteration=iter] <confdb|transdb>\n\n";
  }
  Log scalar(localtime).": STARTED db_update \n";
  db_connect;
  my %ver=load_version($db);
  Log(Dumper \%ver);
  opendir(SQL,"$SQLPATH/$db") || Fatal("Cannot read SQLPATH/$db");
  my @scripts= sort {
                  my @a=$a=~/^(\d+)\.(\d+)\.(\d+)-(\d+)-(\S+)\.sql$/;
                  my @b=$b=~/^(\d+)\.(\d+)\.(\d+)-(\d+)-(\S+)\.sql$/;
                  ($a[0] <=> $b[0]) || ($a[1] <=> $b[1]) || ($a[2] <=> $b[2]) || ($a[3] <=> $b[3]) || ($a[4] cmp $b[4])
               }  grep   {  /^(\d+)\.(\d+)\.(\d+)-(\d+)-(\S+)\.sql$/ } readdir(SQL);
  close SQL;
  my $applied_mark='';
  foreach my $sql (@scripts) {
    $sql=~/^(\d+)\.(\d+)\.(\d+)-(\d+)-(\S+)\.sql$/;
    my $cmp=($1<=>$ver{MAJOR}) || ($2<=>$ver{MINOR}) || ($3<=>$ver{PATCH})  || ($4<=>$ver{ITERATION});
    if($cmp<=0){
      Log "skipping: $sql\n";
      next;
    }
    my $mark="$1.$2.$3-$4"; 
    save_version($db,$applied_mark) if $mark ne $applied_mark;
    apply($db,$sql);
    $applied_mark=$mark;
  }
  save_version($db,$applied_mark);

END {
 Log scalar(localtime).": FINISHED db_update \n";
}
