package DB::Introspector::Oracle::Introspector;

use strict;

use base qw( DB::Introspector::CommonRDB::Introspector );


use constant SINGLE_TABLE_QUERY => 
q( SELECT LOWER(table_name) AS TABLE_NAME, LOWER(user) AS owner_name 
   FROM user_tables WHERE LOWER(table_name)=? );

use constant ALL_TABLES_QUERY => 
q( SELECT LOWER(table_name) AS table_name, LOWER(USER) AS owner_name 
   FROM user_tables );


sub _cached_table {
    my $self = shift;
    my $table_name = shift;
    return $self->SUPER::_cached_table(lc($table_name));
}

sub get_single_table_lookup_statement {
    my $self = shift;
    my $table_name = lc(shift);

    my $sth = $self->dbh->prepare_cached(SINGLE_TABLE_QUERY);
    $sth->execute($table_name);
    return $sth;
}

sub get_all_tables_lookup_statement {
    my $self = shift;
    my $sth = $self->dbh->prepare_cached(ALL_TABLES_QUERY);
    $sth->execute();
    return $sth;
}

sub get_table_class {
    return q(DB::Introspector::Oracle::Table);
}


package DB::Introspector::Oracle::Table;

use strict;

use base qw( DB::Introspector::CommonRDB::Table );

use constant COLUMN_LOOKUP_QUERY =>
q(SELECT LOWER(column_name) AS NAME, data_type AS TYPE FROM user_tab_columns
  WHERE LOWER(table_name)=?);

use constant FOREIGN_KEYS_LOOKUP_QUERY =>
q(SELECT constraint_name AS name FROM user_constraints 
  WHERE constraint_type='R' AND LOWER(table_name)=?);

use constant DEPENDENCIES_LOOKUP_QUERY =>
q(SELECT dep.constraint_name AS name, dep.table_name AS child_table_name
  FROM  user_constraints dep, 
        user_constraints ent
  WHERE dep.constraint_type = 'R'
    AND dep.r_constraint_name = ent.constraint_name
    AND LOWER(ent.table_name) = ?);

use constant PRIMARY_KEY_LOOKUP_QUERY =>
q(SELECT LOWER(column_name) FROM user_cons_columns WHERE constraint_name IN (
    SELECT constraint_name FROM user_constraints 
        WHERE LOWER(table_name)=? 
          AND constraint_type='P'
 ) ORDER BY position); 

use DB::Introspector::Base::BooleanColumn;
use DB::Introspector::Base::IntegerColumn;
use DB::Introspector::Base::CharColumn;
use DB::Introspector::Base::CLOBColumn;
use DB::Introspector::Base::StringColumn;
use DB::Introspector::Base::DateTimeColumn;

use constant COLUMN_CLASS_MAPPING => {
    'NUMBER' => 'DB::Introspector::Base::IntegerColumn',
    'LONG' => 'DB::Introspector::Base::IntegerColumn',
    'CHAR' => 'DB::Introspector::Base::CharColumn',
    'VARCHAR' => 'DB::Introspector::Base::StringColumn',
    'VARCHAR2' => 'DB::Introspector::Base::StringColumn',
    'DATE' => 'DB::Introspector::Base::DateTimeColumn',
    'TIMESTAMP(6)' => 'DB::Introspector::Base::DateTimeColumn',
    'CLOB' => 'DB::Introspector::Base::CLOBColumn',
};

sub get_primary_key_column_ids {
    my $self = shift;

    unless( $self->{_primary_key_column_ids} ) {
        my $sth =
          $self->_introspector->dbh->prepare_cached(PRIMARY_KEY_LOOKUP_QUERY);
        $sth->execute($self->name);
        my @columns = $self->columns; 
        my @ids;
        while( my ($column_name) = $sth->fetchrow_array ) {
            COLUMN_WALK: foreach my $i (0..$#columns) {
                if( $columns[$i]->name eq $column_name ) {
                    push(@ids, $i);
                    last COLUMN_WALK;
                }
            }
        }
        $sth->finish();
        $self->{_primary_key_column_ids} = \@ids;
    }
    return @{$self->{_primary_key_column_ids}};
}

sub get_column_lookup_statement {
    my $self = shift;

    my $sth = $self->_introspector->dbh->prepare_cached(COLUMN_LOOKUP_QUERY);
    $sth->execute(lc($self->name));

    return $sth;
}

sub get_foreign_keys_lookup_statement {
    my $self = shift;

    my $sth = $self->_introspector->dbh->prepare_cached(
        FOREIGN_KEYS_LOOKUP_QUERY);

    $sth->execute(lc($self->name));
    return $sth;
}

sub get_dependencies_lookup_statement {
    my $self = shift;

    my $sth = $self->_introspector->dbh->prepare_cached(
        DEPENDENCIES_LOOKUP_QUERY);

    $sth->execute(lc($self->name));
    return $sth;
}

sub get_column_instance {
    my $self = shift;
    my $name = shift;
    my $type_name = uc(shift);

    my $class = COLUMN_CLASS_MAPPING()->{$type_name}
                            || die("class not found for type:$type_name");
    return $class->new($name);
}

sub get_foreign_key_class {
    return q(DB::Introspector::Oracle::ForeignKey);
}



package DB::Introspector::Oracle::ForeignKey;

use strict;

use base qw( DB::Introspector::CommonRDB::ForeignKey );

use constant LOCAL_COLUMN_NAME_QUERY => 
q(SELECT LOWER(column_name) AS NAME FROM user_cons_columns 
  WHERE constraint_name=?  ORDER BY position);

use constant FOREIGN_COLUMN_NAME_QUERY =>
q( SELECT /*+first_rows*/ LOWER(column_name) AS name
    FROM    user_constraints u,
            user_cons_columns c
    WHERE c.constraint_name=u.r_constraint_name
      AND u.constraint_name=? ORDER BY position);

use constant FOREIGN_TABLE_NAME_QUERY => 
q(SELECT LOWER(R.TABLE_NAME) AS NAME 
    FROM USER_CONSTRAINTS R, USER_CONSTRAINTS U 
    WHERE U.R_CONSTRAINT_NAME=R.CONSTRAINT_NAME
      AND U.CONSTRAINT_NAME=? );

sub new {
    my $class = shift;

    my $self = $class->SUPER::new(@_);

    my $table = shift;
    my $dependency = shift;
    my %args = @_;
    $self->{'_name'} = $args{'NAME'};
    return $self;
}

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

sub get_local_column_name_lookup_statement {
    my $self = shift;
    my $sth = $self->local_table->_introspector->dbh->prepare_cached(
        LOCAL_COLUMN_NAME_QUERY);
    $sth->execute($self->name);
    return $sth;
}

sub get_foreign_column_name_lookup_statement {
    my $self = shift;
    my $sth = $self->local_table->_introspector->dbh->prepare_cached(
        FOREIGN_COLUMN_NAME_QUERY);
    $sth->execute($self->name);
    return $sth;
}

sub get_foreign_table_name_lookup_statement {
    my $self = shift;
    my $sth = $self->local_table->_introspector->dbh->prepare_cached(
        FOREIGN_TABLE_NAME_QUERY);
    $sth->execute($self->name);
    return $sth;
}

1;
