#################################################################################
#
# $Header: SQLFile.pm 10-aug-2006.21:41:27 chyu Exp $
#
# SQLFile.pm
#
# Copyright (c) 2003, 2006, Oracle. All rights reserved.  
#
#    NAME
#      SQLFile.pm - <one-line expansion of the name>
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YYYY)
#       chyu       08/10/06 - adding the condition flag
#       gsbhatia   12/15/05 - bug 4888848 
#       chyu       11/14/05 - XbranchMerge chyu_bug-4703513 from main 
#       chyu       10/28/05 - using the EMDW_HOME for SQLFile root 
#       tthakur    08/08/05 - adding post_data_upgrade 
#       gsbhatia   07/26/05 - bug 4515615 
#       gsbhatia   07/22/05 - Add getters/setters 
#       gsbhatia   07/19/05 - Refactor code 
#       gsbhatia   07/16/05 - Add logging support 
#       gsbhatia   06/26/05 - New repmgr impl 
#       gsbhatia   04/11/05 - Adding header accessor API 
#       gsbhatia   01/20/05 - change comparison string from migrate to upgrade 
#       gsbhatia   01/18/05 - fill skeleton code 
#       ktlaw      01/14/05 - add comments 
#       ktlaw      01/13/05 - ktlaw_new_repmgr
#       ktlaw      01/10/05 - created
################################################################################

package SQLFile;

use strict;
use File::Basename;
use Logger;

sub log{
  my ($self, $msg) = @_;
  $self->getLogger->infoln(qq($msg));
}

sub getLogger{
  my $self = shift;
  return $self->{'logger'};
}

sub setLogger{
  my ($self, $v) = @_;
  $self->{'logger'} = $v;
}

sub getIsValid{
  my $self = shift;
  return $self->{'isValid'};
}

sub setIsValid{
  my ($self, $v) = @_;
  $self->{'isValid'} = $v;
}

sub getPath{
  my $self = shift;
  return $self->{'path'};
}

sub setPath{
  my ($self, $v) = @_;
  $self->{'path'} = $v;
}

sub getComponentRoot{
  my $self = shift;
  return $self->{'componentRoot'};
}

sub setComponentRoot{
  my ($self, $v) = @_;
  $self->{'componentRoot'} = $v;
}

sub getBasedir{
  my $self = shift;
  return $self->{'basedir'};
}

sub setBasedir{
  my ($self, $v) = @_;
  $self->{'basedir'} = $v;
}

sub getPathRelativeToBasedir{
  my $self = shift;
  return $self->{'pathRelativeToBasedir'};
}

sub setPathRelativeToBasedir{
  my ($self, $v) = @_;
  $self->{'pathRelativeToBasedir'} = $v;
}

sub getComponent{
  my $self = shift;
  return $self->{'component'};
}

sub setComponent{
  my ($self, $v) = @_;
  $self->{'component'} = $v;
}

sub getFu{
  my $self = shift;
  return $self->{'fu'};
}

sub setFu{
  my ($self, $v) = @_;
  $self->{'fu'} = $v;
}

sub getExt{
  my $self = shift;
  return $self->{'ext'};
}

sub setExt{
  my ($self, $v) = @_;
  $self->{'ext'} = $v;
}

sub getDir{
  my $self = shift;
  return $self->{'dir'};
}

sub setDir{
  my ($self, $v) = @_;
  $self->{'dir'} = $v;
}

sub getName{
  my $self = shift;
  return $self->{'name'};
}

sub setName{
  my ($self, $v) = @_;
  $self->{'name'} = $v;
}

sub getParams{
  my $self = shift;
  return @{$self->{'params'}};
}

sub setParams{
  my ($self, $v) = @_;
  $self->{'params'} = \$v;
}

sub getParamsRef{
  my $self = shift;
  return $self->{'params'};
}

sub setParamsRef{
  my ($self, $v) = @_;
  $self->{'params'} = $v;
}

sub getOperation{
  my $self = shift;
  return $self->{'operation'};
}

sub setOperation{
  my ($self, $v) = @_;
  $self->{'operation'} = $v;
}

sub getHeader{
  my $self = shift;
  return $self->{'header'};
}

sub setHeader{
  my ($self, $v) = @_;
  $self->{'header'} = $v;
}

sub getSeq{
  my $self = shift;
  return $self->{'seq'};
}

sub setSeq{
  my ($self, $v) = @_;
  $self->{'seq'} = $v;
}

sub getSequence{
  my $self = shift;
  return $self->getSeq;
}

sub setSequence{
  my ($self, $v) = @_;
  $self->setSeq($v);
}

sub getPos{
  my $self = shift;
  return $self->{'pos'};
}

sub setPos{
  my ($self, $v) = @_;
  $self->{'pos'} = $v;
}

sub setCondition{
  my ($self, $v) = @_;
  my %conditionHash = split(/[=;]/,$v);
  foreach my $k (keys %conditionHash) {
    $self->{'condition'}{$k} = $conditionHash{$k};
  }
}

sub hasConditions{
  my $self = shift;
  if (defined $self->{'condition'}) {
    return 1;
  }
  return 0;
}

sub getConditionKeys{
  my $self = shift;
  if (defined $self->{'condition'}) {
    my $hash = $self->{'condition'};
    return keys(%$hash);
  }
  return ();
}

sub getCondition{
  my ($self, $v) = @_;
  if (defined $self->{'condition'}) {
    return $self->{'condition'}{$v};
  }
  return undef;
}

sub getVer{
  my $self = shift;
  return $self->{'ver'};
}

sub setVer{
  my ($self, $v) = @_;
  $self->{'ver'} = $v;
}

sub getVersion{
  my $self = shift;
  return $self->getVer;
}

sub setVersion{
  my ($self, $v) = @_;
  $self->setVer($v);
}

sub getType{
  my $self = shift;
  return $self->{'type'};
}

sub setType{
  my ($self, $v) = @_;
  $self->{'type'} = $v;
}

sub getAfuDir{
  my $self = shift;
  return $self->{'afuDir'};
}

sub setAfuDir{
  my ($self, $v) = @_;
  $self->{'afuDir'} = $v;
}

sub getAfuName{
  my $self = shift;
  return $self->{'afuName'};
}

sub setAfuName{
  my ($self, $v) = @_;
  $self->{'afuName'} = $v;
}

sub getAfuExt{
  my $self = shift;
  return $self->{'afuExt'};
}

sub setAfuExt{
  my ($self, $v) = @_;
  $self->{'afuExt'} = $v;
}

sub getAfuProcessed{
  my $self = shift;
  return $self->{'afuProcessed'};
}

sub setAfuProcessed{
  my ($self, $v) = @_;
  $self->{'afuProcessed'} = $v;
}

sub new{
  my ($class) = @_;
  my $ref = {}; 
  bless $ref, $class;
  $ref->setLogger(Logger->new());
  return $ref;
}

sub init{
  my ($ref, $p) = @_;  
  
  #Assume the sql is invalid by default.
  #If it turns out to valid, it will be marked so later.
  $ref->setIsValid(0);
  $ref->setPath($p);

  my $oracleHome = $ENV{'EMDW_HOME'};
  if ( $oracleHome eq "" )
  {
    $oracleHome = $ENV{'ORACLE_HOME'};
  }
  my $sqlHome = "$oracleHome/sysman/admin/emdrep/sql";

  #Get component name
  my $comp;
  if ($ref->getComponentRoot =~ m/$sqlHome\/(.+)/){
    #If can't even find component home, return
    #In this case sql has already been marked as invalid
    if (!defined $1){ return;}
    $comp = $1;
  }
  if (!defined $comp){ return;}

  my $regex = qr/$sqlHome\/$comp\/(.+?)\/((?:(.+)\/)*(.+)\.(sql|plb))$/;
  #Consider the sql for further processing only if it matches
  #the file location pattern.
  #For instance, file must be located in this fashion:
  #$SQLROOT/<component home>/basedir/... file can now be anywhere ..
  #All position attribute references are relative to the basedir
  #Directory immediately after base dir is the functional unit directory
  #Finally, at the end, we get the file under consideration
  if($p =~ $regex){
    $ref->setBasedir($1);
    $ref->setPathRelativeToBasedir($2);
    $ref->setComponent( $comp) ;
    if (defined $3){
      $ref->setFu($3) ;
    }else{
      $ref->setFu('');
    }
    $ref->setExt($5);
    $ref->setName(basename($p));
    $ref->setDir(dirname($p));
    #Initialize the afu
    $ref->setAfuDir($ref->createAfuDir($ref->getFu));
    $ref->setAfuName($4);
    $ref->setAfuExt($ref->getExt);
    $ref->setAfuProcessed(0);
    
    #Parse the header
    $ref->parse;
  }
}

#This creates the afuDir data structure
sub createAfuDir{
  my ($self, $str) = @_;
  my @a = split(/\//, $str);
  return \@a;
}

sub trimLine
{
  my $string = shift;
  $string =~ s/^\s+//;
  $string =~ s/\s+$//;
  return $string;
}

#Parse the header and fetch the details
sub parse
{
  my $ref = shift ;  
  if(defined $ref->getPath)
  {
    if(open(F,$ref->getPath))
    {
      while(<F>)
      {
        chomp ($_ = &trimLine($_));
        #Construct the repmgr header driver regex
        my $headerRegex = qr/^\s*[rR][eE][mM]\s+[dD][rR][vV]\s*:\s*(.*?)\s*$/;
        if($_ =~ $headerRegex)
        {
	  my $header = $1;
          #Construct the create header regex
          my $createRegex = qr/^<\s*create\s+type\s*=\s*
              (?:'|")(types|tables|indexes|procs|funcs|triggers|views|pkgdefs|pkgbodys|init|type_bodys|synonyms|post_creation|out_of_box)(?:'|")
		(?:\s+seq\s*=\s*(?:"|')(.+?)(?:"|'))?
		(?:\s+params\s*=\s*(?:"|')(.+?)(?:"|'))?
		(?:\s+pos\s*=\s*(?:"|')(.+?)(?:"|'))?	
                (?:\s+condition\s*=\s*(?:"|')(.+?)(?:"|'))?
		\s*\/>/x;
          #Construct the migrate header regex
          my $migrateRegex=qr/^<\s*migrate\s+type\s*=\s*
                 (?:'|")(schema_upgrade|data_upgrade|schema_downgrade|data_downgrade|pre_data_upgrade|pre_schema_upgrade|pre_schema_downgrade|pre_data_downgrade|post_data_upgrade)(?:'|")
		(?:\s+version\s*=\s*(?:'|")(.+?)(?:'|"))?
 		(?:\s+seq\s*=\s*(?:'|")(.+?)(?:'|"))?
		(?:\s+params\s*=\s*(?:"|')(.+?)(?:"|'))?
		(?:\s+pos\s*=\s*(?:'|")(.+?)(?:'|"))?	
                (?:\s+condition\s*=\s*(?:"|')(.+?)(?:"|'))?
		\s*\/>/x;
          my $paramsRegex = qr/
		    EM_SQL_ROOT|EM_REPOS_USER|EM_REPOS_PWD|
                    EM_REPOS_MODE|EM_TEMP_TABLESPACE_NAME|EM_TABLESPACE_NAME|
                    EM_DEFAULT_DATAFILE_NAME|EM_DEFAULT_DATAFILE_INIT_SIZE|
                    EM_DEFAULT_DATAFILE_EXTEND_SIZE|EM_ECM_DEPOT_TABLESPACE|
                    EM_ECM_DATAFILE_NAME|EM_ECM_DATAFILE_INIT_SIZE|
            	    EM_ECM_CSA_TABLESPACE_NAME|EM_ECM_CSA_DATAFILE_NAME|
		    EM_ECM_CSA_DATAFILE_SIZE|EM_ECM_CSA_DATAFILE_EXT_SIZE|
                    EM_ECM_DATAFILE_EXTEND_SIZE|EM_ECHO_SQL
		    /x;
          #Check if header matches create's pattern
	  if ($header =~ $createRegex){
            #Now the SQL is guaranteed to be valid, mark it so.
            $ref->setIsValid(1);
	    $ref->setOperation('create');
            $ref->setHeader($header);
            $ref->setType($1);
            if (defined $2){ $ref->setSeq($2);}
            else{ $ref->setSeq(0);}
            my @params;
            #set params in the object model only if they are valid
            if (defined $3){
              @params = split /,/, $3;
              for (@params){
                if (!$paramsRegex){
                  undef @params;
                  last;
                }                                    
              }
            }
	    if(defined @params){
              $ref->setParamsRef(\@params); 
            }else{
	      $ref->setParamsRef([]);
	    }
	    $ref->setPos($4) if (defined $4);
            $ref->setCondition($5) if (defined $5);
            last;
	  }
          #Check if the header instead matches the migrate pattern
	  elsif ($header =~ $migrateRegex){
            #Default SQL to be valid, marking it so.
            $ref->setIsValid(1);
	    $ref->setOperation('migrate');
            $ref->setHeader($header);
            $ref->setType($1);
	   if (defined $2) {$ref->setVer($2);}
	   #only post_data_upgrade types are version neutral.O/w mark sql as invalid.
	   if (($ref->getType() ne qw(post_data_upgrade)) && !(defined($ref->getVer()))) {$ref->setIsValid(0)};
            if (defined $3){ $ref->setSeq($3);}
            else{ $ref->setSeq(0);}
            my @params;
            #set params in the object model only if they are valid
            if (defined $4){
              @params = split /,/, $4;
              for (@params){
                if (!$paramsRegex){
                  undef @params;
                  last;
                }
              }
            }
            if(defined @params){
              $ref->setParamsRef(\@params);
            }else{
              $ref->setParamsRef([]);
            }

	    $ref->setPos($5) if (defined $5);
            $ref->setCondition($6) if (defined $6);
 	    last;
	  }
        }
      }
      close(F);
    }else
    {      
      #log error and die?
      #$ref->getLogger->warnAndDie("Can't open file: $ref->getPath");
      #Don't die but issue an error instead    
      $ref->log("ERROR: Can't open file: $ref->getPath");
      $ref->log("ERROR: Note: $ref->getPath will not be queued for execution by RepMgr");
    }
  }else
  {
    #log error and die?
      #Don't die but issue an error instead    
      $ref->log("ERROR: Doesn't exist: $ref->getPath");
  }
}

#The sql object dump for debugging purposes
sub dumpObject
{
  my $ref = shift;
  $ref->log("");
  if ($ref->getIsValid){ 
    foreach my $key (keys %$ref){
	my $val = $ref->{$key};
	$ref->log(qq($key: $val)) if (!ref($val));
	$ref->log(qq($key: @$val)) if (ref($val) eq 'ARRAY');
    } 
  }else{
    $ref->log(qq($ref->getPath));
    $ref->getLogger->warn("Invalid object"); 
  } 
  $ref->log("");
}

1;
