The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

use strict;

use Test::More tests => 11;

BEGIN 
{
  require 't/test-lib.pl';
  use_ok('Rose::DB::Object::Loader');
}

our %Have;

#
# Tests
#

#$Rose::DB::Object::Manager::Debug = 1;

foreach my $db_type (qw(mysql pg pg_with_schema informix sqlite))
{
  SKIP:
  {
    skip("$db_type tests", 2)  unless($Have{$db_type});
  }

  next  unless($Have{$db_type});

  Rose::DB->default_type($db_type);

  my $class_prefix = 
    ucfirst($db_type eq 'pg_with_schema' ? 'pgws' : $db_type) . 'MusicDB';

  my $loader = 
    Rose::DB::Object::Loader->new(
      db           => Rose::DB->new,
      class_prefix => $class_prefix);

  my @classes = $loader->make_classes(include_tables => 'rdbo_album.*');

  #foreach my $class (@classes)
  #{
  #  print $class->meta->perl_class_definition(braces => 'k&r', indent => 2)
  #    if($class->can('meta'));
  #}

  my $manager_class = $class_prefix . '::RdboAlbumArtwork::Manager';

  my $results = 
    $manager_class->get_rdbo_album_artwork(
      query   => [ art_filename => 'album1.jpg' ],
      sort_by => 'art_filename');

  foreach my $res (@$results) 
  {
    my $album = $res->album;
    is($album->name, 'album1', "album 1 - $db_type");
  }

  LEAK_TEST:
  {
    $RDBO::LeakTest = 0;

    my $db_class = ref(Rose::DB->new);

    no strict 'refs';
    no warnings 'redefine';
    *{"${db_class}::DESTROY"} = sub
    {
      $_[0]->disconnect;
      $RDBO::LeakTest++;
    };

    INNER:
    {
      my $iter = 
        $manager_class->get_rdbo_album_artwork_iterator(
          query   => [ art_filename => 'album1.jpg' ],
          sort_by => 'art_filename');

      while(my $res = $iter->next)
      {
        # do nothing
      }
    }

    is($RDBO::LeakTest, 1, "iterator db leak check - $db_type");
  }
}

BEGIN
{
  our %Have;

  #
  # PostgreSQL
  #

  my $dbh;

  eval 
  {
    $dbh = Rose::DB->new('pg_admin')->retain_dbh()
      or die Rose::DB->error;
  };

  if(!$@ && $dbh)
  {
    $Have{'pg'} = 1;
    $Have{'pg_with_schema'} = 1;

    # Drop existing tables and create schema, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rdbo_album_artwork CASCADE');
      $dbh->do('DROP TABLE rdbo_albums CASCADE');
      $dbh->do('DROP TABLE Rose_db_object_private.rdbo_album_artwork CASCADE');
      $dbh->do('DROP TABLE Rose_db_object_private.rdbo_albums CASCADE');
      $dbh->do('CREATE SCHEMA Rose_db_object_private');
    }

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_albums 
(
  id        INTEGER PRIMARY KEY,
  other_id  VARCHAR(32) UNIQUE,
  name      VARCHAR(32),
  artist    VARCHAR(32),
  year      INTEGER
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_album_artwork 
(
  art_filename   VARCHAR(32) PRIMARY KEY,
  album_other_id VARCHAR(32) REFERENCES rdbo_albums (other_id)
)
EOF

    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (1, 'id1', 'album1', 'artist1', 1999)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (2, 'id2', 'album2', 'artist1', 2000)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (3, 'id3', 'album3', 'artist2', 1934)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (4, 'id4', 'album4', 'artist2', 2020)));

    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album1.jpg', 'id1')));
    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album2.jpg', 'id2')));

    $dbh->do(<<"EOF");
CREATE TABLE Rose_db_object_private.rdbo_albums 
(
  id        INTEGER PRIMARY KEY,
  other_id  VARCHAR(32) UNIQUE,
  name      VARCHAR(32),
  artist    VARCHAR(32),
  year      INTEGER
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE Rose_db_object_private.rdbo_album_artwork 
(
  art_filename   VARCHAR(32) PRIMARY KEY,
  album_other_id VARCHAR(32) REFERENCES Rose_db_object_private.rdbo_albums (other_id)
)
EOF

    $dbh->do(qq(INSERT INTO Rose_db_object_private.rdbo_albums VALUES (1, 'id1', 'album1', 'artist1', 1999)));
    $dbh->do(qq(INSERT INTO Rose_db_object_private.rdbo_albums VALUES (2, 'id2', 'album2', 'artist1', 2000)));
    $dbh->do(qq(INSERT INTO Rose_db_object_private.rdbo_albums VALUES (3, 'id3', 'album3', 'artist2', 1934)));
    $dbh->do(qq(INSERT INTO Rose_db_object_private.rdbo_albums VALUES (4, 'id4', 'album4', 'artist2', 2020)));

    $dbh->do(qq(INSERT INTO Rose_db_object_private.rdbo_album_artwork VALUES ('album1.jpg', 'id1')));
    $dbh->do(qq(INSERT INTO Rose_db_object_private.rdbo_album_artwork VALUES ('album2.jpg', 'id2')));

    $dbh->disconnect;
  }

  #
  # MySQL
  #

  eval 
  {
    my $db = Rose::DB->new('mysql_admin');
    $dbh = $db->retain_dbh or die Rose::DB->error;

    die "MySQL version too old"  unless($db->database_version >= 4_000_000);

    # Drop existing tables, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rdbo_album_artwork CASCADE');
      $dbh->do('DROP TABLE rdbo_albums CASCADE');
    }

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_albums 
(
  id        INTEGER PRIMARY KEY,
  other_id  VARCHAR(32) UNIQUE,
  name      VARCHAR(32),
  artist    VARCHAR(32),
  year      INTEGER
)
ENGINE=InnoDB
EOF

    # MySQL will silently ignore the "ENGINE=InnoDB" part and create
    # a MyISAM table instead.  MySQL is evil!  Now we have to manually
    # check to make sure an InnoDB table was really created.
    my $db_name = $db->database;
    my $sth = $dbh->prepare("SHOW TABLE STATUS FROM `$db_name` LIKE ?");
    $sth->execute('rdbo_albums');
    my $info = $sth->fetchrow_hashref;

    no warnings 'uninitialized';
    unless(lc $info->{'Type'} eq 'innodb' || lc $info->{'Engine'} eq 'innodb')
    {
      die "Missing InnoDB support";
    }
  };

  if(!$@ && $dbh)
  {
    $Have{'mysql'} = 1;

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_album_artwork 
(
  art_filename   VARCHAR(32) PRIMARY KEY,
  album_other_id VARCHAR(32),

  INDEX(album_other_id),

  FOREIGN KEY (album_other_id) REFERENCES rdbo_albums (other_id)
)
ENGINE=InnoDB
EOF

    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (1, 'id1', 'album1', 'artist1', 1999)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (2, 'id2', 'album2', 'artist1', 2000)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (3, 'id3', 'album3', 'artist2', 1934)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (4, 'id4', 'album4', 'artist2', 2020)));

    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album1.jpg', 'id1')));
    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album2.jpg', 'id2')));

    $dbh->disconnect;
  }

  #
  # Informix
  #

  eval
  {
    $dbh = Rose::DB->new('informix_admin')->retain_dbh()
      or die Rose::DB->error;
  };

  if(!$@ && $dbh)
  {
    $Have{'informix'} = 1;

    # Drop existing tables and create schema, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rdbo_album_artwork CASCADE');
      $dbh->do('DROP TABLE rdbo_albums CASCADE');
    }

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_albums 
(
  id        INTEGER PRIMARY KEY,
  other_id  VARCHAR(32) UNIQUE,
  name      VARCHAR(32),
  artist    VARCHAR(32),
  year      INTEGER
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_album_artwork 
(
  art_filename   VARCHAR(32) PRIMARY KEY,
  album_other_id VARCHAR(32) REFERENCES rdbo_albums (other_id)
)
EOF

    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (1, 'id1', 'album1', 'artist1', 1999)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (2, 'id2', 'album2', 'artist1', 2000)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (3, 'id3', 'album3', 'artist2', 1934)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (4, 'id4', 'album4', 'artist2', 2020)));

    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album1.jpg', 'id1')));
    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album2.jpg', 'id2')));

    $dbh->disconnect;
  }

  #
  # SQLite
  #

  eval
  {
    $dbh = Rose::DB->new('sqlite_admin')->retain_dbh()
      or die Rose::DB->error;
  };

  if(!$@ && $dbh)
  {
    $Have{'sqlite'} = 1;

    # Drop existing tables and create schema, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rdbo_album_artwork CASCADE');
      $dbh->do('DROP TABLE rdbo_albums CASCADE');
    }

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_albums 
(
  id        INTEGER PRIMARY KEY,
  other_id  VARCHAR(32) UNIQUE,
  name      VARCHAR(32),
  artist    VARCHAR(32),
  year      INTEGER
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rdbo_album_artwork 
(
  art_filename   VARCHAR(32) PRIMARY KEY,
  album_other_id VARCHAR(32) REFERENCES rdbo_albums (other_id)
)
EOF

    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (1, 'id1', 'album1', 'artist1', 1999)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (2, 'id2', 'album2', 'artist1', 2000)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (3, 'id3', 'album3', 'artist2', 1934)));
    $dbh->do(qq(INSERT INTO rdbo_albums VALUES (4, 'id4', 'album4', 'artist2', 2020)));

    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album1.jpg', 'id1')));
    $dbh->do(qq(INSERT INTO rdbo_album_artwork VALUES ('album2.jpg', 'id2')));

    $dbh->disconnect;
  }
}

END
{
  # Delete test table

  if($Have{'pg'})
  {
    # PostgreSQL
    my $dbh = Rose::DB->new('pg_admin')->retain_dbh()
      or die Rose::DB->error;

    $dbh->do('DROP TABLE rdbo_album_artwork CASCADE');
    $dbh->do('DROP TABLE rdbo_albums CASCADE');
    $dbh->do('DROP TABLE Rose_db_object_private.rdbo_album_artwork CASCADE');
    $dbh->do('DROP TABLE Rose_db_object_private.rdbo_albums CASCADE');
    $dbh->do('DROP SCHEMA Rose_db_object_private CASCADE');

    $dbh->disconnect;
  }

  if($Have{'mysql'})
  {
    # MySQL
    my $dbh = Rose::DB->new('mysql_admin')->retain_dbh()
      or die Rose::DB->error;

    $dbh->do('DROP TABLE rdbo_album_artwork CASCADE');
    $dbh->do('DROP TABLE rdbo_albums CASCADE');

    $dbh->disconnect;
  }

  if($Have{'informix'})
  {
    # Informix
    my $dbh = Rose::DB->new('informix_admin')->retain_dbh()
      or die Rose::DB->error;

    $dbh->do('DROP TABLE rdbo_album_artwork CASCADE');
    $dbh->do('DROP TABLE rdbo_albums CASCADE');

    $dbh->disconnect;
  }

  if($Have{'sqlite'})
  {
    # Informix
    my $dbh = Rose::DB->new('sqlite_admin')->retain_dbh()
      or die Rose::DB->error;

    $dbh->do('DROP TABLE rdbo_album_artwork');
    $dbh->do('DROP TABLE rdbo_albums');

    $dbh->disconnect;
  }
}