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 => 1603;

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

our($PG_HAS_CHKPASS, $HAVE_PG, $HAVE_MYSQL, $HAVE_INFORMIX, $HAVE_SQLITE);


use FindBin qw($Bin);

eval { require "$Bin/map-record-name-conflict.pl" };

ok($@ =~ /^\QAlready made a map record method named map_record in class JCS::B on behalf of the relationship 'bs' in class JCS::A.  Please choose another name for the map record method for the relationship named 'bs' in JCS::C.\E/,
   'many-to-many map record name conflict');

#
# PostgreSQL
#

SKIP: foreach my $db_type ('pg')
{
  skip("PostgreSQL tests", 398)  unless($HAVE_PG);

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

  my $o = MyPgObject->new(name => 'John');

  ok(ref $o && $o->isa('MyPgObject'), "new() 1 - $db_type");

  $o->flag2('true');
  $o->date_created('now');
  $o->last_modified($o->date_created);
  $o->save_col(7);

  ok($o->save, "save() 1 - $db_type");
  ok($o->load, "load() 1 - $db_type");

  my $o_x = MyPgObject->new(id => 99, name => 'John X', flag => 0);
  $o_x->save;

  my $o2 = MyPgObject->new(id => $o->id);

  ok(ref $o2 && $o2->isa('MyPgObject'), "new() 2 - $db_type");

  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");

  ok($o2->load, "load() 2 - $db_type");
  ok(!$o2->not_found, "not_found() 1 - $db_type");

  is($o2->name, $o->name, "load() verify 1 - $db_type");
  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
  is($o2->save_col, 7, "load() verify 7 (aliased column) - $db_type");
  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");

  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");

  $o2->name('John 2');
  $o2->start('5/24/2001');

  sleep(1); # keep the last modified dates from being the same

  $o2->last_modified('now');
  ok($o2->save, "save() 2 - $db_type");
  ok($o2->load, "load() 3 - $db_type");

  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");

  my $o3 = MyPgObject->new();

  my $db = $o3->db or die $o3->error;

  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");

  is($db->dbh, $o3->dbh, "dbh() - $db_type");

  my $o4 = MyPgObject->new(id => 999);
  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
  ok($o4->not_found, "not_found() 2 - $db_type");

  ok($o->load, "load() 4 - $db_type");

  SKIP:
  {
    if($PG_HAS_CHKPASS)
    {
      $o->{'password_encrypted'} = ':8R1Kf2nOS0bRE';

      ok($o->password_is('xyzzy'), "chkpass() 1 - $db_type");
      is($o->password, 'xyzzy', "chkpass() 2 - $db_type");

      $o->password('foobar');

      ok($o->password_is('foobar'), "chkpass() 3 - $db_type");
      is($o->password, 'foobar', "chkpass() 4 - $db_type");

      ok($o->save, "save() 3 - $db_type");
    }
    else
    {
      skip("chkpass tests", 5);
    }
  }

  my $o5 = MyPgObject->new(id => $o->id);

  ok($o5->load, "load() 5 - $db_type");

  SKIP:
  {
    if($PG_HAS_CHKPASS)
    {
      ok($o5->password_is('foobar'), "chkpass() 5 - $db_type");
      is($o5->password, 'foobar', "chkpass() 6 - $db_type"); 
    }
    else
    {
      skip("chkpass tests", 2);
    }
  }

  $o5->nums([ 4, 5, 6 ]);
  ok($o5->save, "save() 4 - $db_type");
  ok($o->load, "load() 6 - $db_type");

  is($o5->nums->[0], 4, "load() verify 10 (array value) - $db_type");
  is($o5->nums->[1], 5, "load() verify 11 (array value) - $db_type");
  is($o5->nums->[2], 6, "load() verify 12 (array value) - $db_type");

  my @a = $o5->nums;

  is($a[0], 4, "load() verify 13 (array value) - $db_type");
  is($a[1], 5, "load() verify 14 (array value) - $db_type");
  is($a[2], 6, "load() verify 15 (array value) - $db_type");
  is(@a, 3, "load() verify 16 (array value) - $db_type");

  my $oo1 = MyPgOtherObject->new(k1 => 1, k2 => 2, k3 => 3, name => 'one');
  ok($oo1->save, "other object save() 1 - $db_type");

  my $oo2 = MyPgOtherObject->new(k1 => 11, k2 => 12, k3 => 13, name => 'two');
  ok($oo2->save, "other object save() 2 - $db_type");

  is($o->other_obj, undef, "other_obj() 1 - $db_type");

  $o->fkone(99);
  $o->fk2(99);
  $o->fk3(99);

  eval { $o->other_obj };
  ok($@, "fatal referential_integrity - $db_type");
  ok(!defined $o->other_obj_osoft, "ok referential_integrity 1 - $db_type");
  ok(!defined $o->other_obj_msoft, "ok referential_integrity 2 - $db_type");

  $o->fkone(1);
  $o->fk2(2);
  $o->fk3(3);

  my $obj = $o->other_obj or warn "# ", $o->error, "\n";

  is(ref $obj, 'MyPgOtherObject', "other_obj() 2 - $db_type");
  is($obj->name, 'one', "other_obj() 3 - $db_type");
  is($obj->db, $o->db, "share_db (default true) - $db_type");

  $o->other_obj(undef);
  $o->fkone(11);
  $o->fk2(12);
  $o->fk3(13);

  ok(!$o->has_loaded_related('other_obj'), "has_loaded_related() 1 - $db_type");

  $obj = $o->other_obj or warn "# ", $o->error, "\n";

  ok($o->has_loaded_related('other_obj'), "has_loaded_related() 2 - $db_type");

  $o->forget_related('other_obj');
  ok(!$o->has_loaded_related('other_obj'), "forget_related() 1 - $db_type");

  $obj = $o->other_obj or warn "# ", $o->error, "\n";
  ok($o->has_loaded_related('other_obj'), "forget_related() 2 - $db_type");

  eval { $o->forget_related(foreign_key => 'other_obj_nonesuch') };
  ok($@, "forget_related() 3 - $db_type");
  $o->forget_related(relationship => 'other_obj');
  ok(!$o->has_loaded_related('other_obj'), "forget_related() 4 - $db_type");

  $obj = $o->other_obj or warn "# ", $o->error, "\n";

  is(ref $obj, 'MyPgOtherObject', "other_obj() 4 - $db_type");
  is($obj->name, 'two', "other_obj() 5 - $db_type");

  my $oo21 = MyPgOtherObject2->new(id => 1, name => 'one', pid => $o->id);
  ok($oo21->save, "other object 2 save() 1 - $db_type");

  my $oo22 = MyPgOtherObject2->new(id => 2, name => 'two', pid => $o->id);
  ok($oo22->save, "other object 2 save() 2 - $db_type");

  my $oo23 = MyPgOtherObject2->new(id => 3, name => 'three', pid => $o_x->id);
  ok($oo23->save, "other object 2 save() 3 - $db_type");

  # Begin filtered collection tests

  my $x = MyPgObject->new(id => $o->id)->load;
  $x->other2_a_objs({ id => 100, name => 'aoo' }, { id => 101, name => 'abc' });

  $x->save;

  $x = MyPgObject->new(id => $o->id)->load;

  my $ao = $x->other2_a_objs;
  my $oo = $x->other2_objs;

  is(scalar @$ao, 2, "filtered one-to-many 1 - $db_type");
  is(join(',', map { $_->name } @$ao), 'abc,aoo', "filtered one-to-many 2 - $db_type");

  is(scalar @$oo, 4, "filtered one-to-many 3 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'abc,aoo,one,two', "filtered one-to-many 4 - $db_type");

  $x->other2_a_objs({ id => 102, name => 'axx' });
  $x->save;

  $x = MyPgObject->new(id => $o->id)->load;

  $ao = $x->other2_a_objs;
  $oo = $x->other2_objs;

  is(scalar @$ao, 1, "filtered one-to-many 5 - $db_type");
  is(join(',', map { $_->name } @$ao), 'axx', "filtered one-to-many 6 - $db_type");

  is(scalar @$oo, 3, "filtered one-to-many 7 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'axx,one,two', "filtered one-to-many 8 - $db_type");

  $x->other2_a_objs([]);
  $x->save;

  # End filtered collection tests

  ok(!$o->has_loaded_related('other2_objs'), "has_loaded_related() 3 - $db_type");

  my $o2s = $o->other2_objs;

  ok($o->has_loaded_related('other2_objs'), "has_loaded_related() 4 - $db_type");

  ok(ref $o2s eq 'ARRAY' && @$o2s == 2 && 
     $o2s->[0]->name eq 'two' && $o2s->[1]->name eq 'one',
     'other objects 1');

  my @o2s = $o->other2_objs;

  ok(@o2s == 2 && $o2s[0]->name eq 'two' && $o2s[1]->name eq 'one',
     'other objects 2');

  my $color = MyPgColor->new(id => 1, name => 'red');
  ok($color->save, "save color 1 - $db_type");

  $color = MyPgColor->new(id => 2, name => 'green');
  ok($color->save, "save color 2 - $db_type");

  $color = MyPgColor->new(id => 3, name => 'blue');
  ok($color->save, "save color 3 - $db_type");

  $color = MyPgColor->new(id => 4, name => 'pink');
  ok($color->save, "save color 4 - $db_type");

  my $map1 = MyPgColorMap->new(obj_id => 1, color_id => 1);
  ok($map1->save, "save color map record 1 - $db_type");

  my $map2 = MyPgColorMap->new(obj_id => 1, color_id => 3);
  ok($map2->save, "save color map record 2 - $db_type");

  my $map3 = MyPgColorMap->new(obj_id => 99, color_id => 4);
  ok($map3->save, "save color map record 3 - $db_type");

  my $colors = $o->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "colors 1 - $db_type");

  $colors = $o->find_colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "find colors 1 - $db_type");

  $colors = $o->find_colors([ name => { like => 'r%' } ]);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red',
     "find colors 2 - $db_type");

  $colors = $o->find_colors(query => [ name => { like => 'r%' } ], cache => 1);

  my $colors2 = $o->find_colors(from_cache => 1);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red' &&
     ref $colors2 eq 'ARRAY' && @$colors2 == 1 && $colors2->[0]->name eq 'red' &&
     $colors->[0] eq $colors2->[0],
     "find colors from cache - $db_type");

  my $count = $o->colors_count;

  is($count, 2, "count colors 1 - $db_type");

  $count = $o->colors_count([ name => { like => 'r%' } ]);

  is($count, 1, "count colors 2 - $db_type");

  my @colors = $o->colors;

  ok(@colors == 2 && $colors[0]->name eq 'blue' && $colors[1]->name eq 'red',
     "colors 2 - $db_type");

  $colors = $o_x->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'pink',
     "colors 3 - $db_type");

  @colors = $o_x->colors;

  ok(@colors == 1 && $colors[0]->name eq 'pink', "colors 4 - $db_type");

  $o = MyPgObject->new(id => 1)->load;
  $o->fkone(1);
  $o->fk2(2);
  $o->fk3(3);
  $o->save;

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

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $o->delete(cascade => 'null');
  };

  ok($@, "delete cascade null 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyPgOtherObject');

  is($count, 2, "delete cascade rollback confirm 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyPgOtherObject2');

  is($count, 3, "delete cascade rollback confirm 2 - $db_type");

  ok($o->delete(cascade => 'delete'), "delete cascade delete 1 - $db_type");

  $o = MyPgObject->new(id => 99)->load;
  $o->fkone(11);
  $o->fk2(12);
  $o->fk3(13);
  $o->save;

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $o->delete(cascade => 'null');
  };

  ok($@, "delete cascade null 2 - $db_type");

  ok($o->delete(cascade => 'delete'), "delete cascade delete 2 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyPgColorMap');

  is($count, 0, "delete cascade confirm 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyPgOtherObject2');

  is($count, 0, "delete cascade confirm 2 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyPgOtherObject');

  is($count, 0, "delete cascade confirm 3 - $db_type");

  eval { $o->meta->alias_column(nonesuch => 'foo') };
  ok($@, "alias_column() nonesuch - $db_type");

  # Start foreign key method tests

  #
  # Foreign key get_set_now
  #

  $o = MyPgObject->new(id   => 50,
                       name => 'Alex',
                       flag => 1);

  eval { $o->other_obj('abc') };
  ok($@, "set foreign key object: one arg - $db_type");

  eval { $o->other_obj(k1 => 1, k2 => 2, k3 => 3) };
  ok($@, "set foreign key object: no save - $db_type");

  $o->save;

  eval
  {
    local $o->db->dbh->{'PrintError'} = 0;
    $o->other_obj(k1 => 1, k2 => 2);
  };

  ok($@, "set foreign key object: too few keys - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 1 - $db_type");
  ok($o->fkone == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 1 - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 2 - $db_type");
  ok($o->fkone == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 2 - $db_type");

  #
  # Foreign key delete_now
  #

  ok($o->delete_other_obj, "delete foreign key object 1 - $db_type");

  ok(!defined $o->fkone && !defined $o->fk2 && !defined $o->fk3, "delete foreign key object check keys 1 - $db_type");

  ok(!defined $o->other_obj && defined $o->error, "delete foreign key object confirm 1 - $db_type");

  ok(!defined $o->delete_other_obj, "delete foreign key object 2 - $db_type");

  #
  # Foreign key get_set_on_save
  #

  # TEST: Set, save
  $o = MyPgObject->new(id   => 100,
                       name => 'Bub',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 21, k2 => 22, k3 => 23), "set foreign key object on save 1 - $db_type");

  my $co = MyPgObject->new(id => 100);
  ok(!$co->load(speculative => 1), "set foreign key object on save 2 - $db_type");

  my $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 3 - $db_type");

  ok($o->save, "set foreign key object on save 4 - $db_type");

  $o = MyPgObject->new(id => 100);

  $o->load;

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 5 - $db_type");

  # TEST: Set, set to undef, save
  $o = MyPgObject->new(id   => 200,
                       name => 'Rose',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 6 - $db_type");

  $co = MyPgObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 7 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 8 - $db_type");

  $o->other_obj_on_save(undef);

  ok($o->save, "set foreign key object on save 9 - $db_type");

  $o = MyPgObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 10 - $db_type");

  $co = MyPgOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 11 - $db_type");

  $o->delete(cascade => 1);

  # TEST: Set, delete, save
  $o = MyPgObject->new(id   => 200,
                       name => 'Rose',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 12 - $db_type");

  $co = MyPgObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 13 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 14 - $db_type");

  ok($o->delete_other_obj, "set foreign key object on save 15 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "set foreign key object on save 16 - $db_type");

  ok($o->save, "set foreign key object on save 17 - $db_type");

  $o = MyPgObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 18 - $db_type");

  $co = MyPgOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 19 - $db_type");

  $o->delete(cascade => 1);

  #
  # Foreign key delete_on_save
  #

  $o = MyPgObject->new(id   => 500,
                       name => 'Kip',
                       flag => 1);

  $o->other_obj_on_save(k1 => 7, k2 => 8, k3 => 9);
  $o->save;

  $o = MyPgObject->new(id => 500);
  $o->load;

  # TEST: Delete, save
  $o->del_other_obj_on_save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 1 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MyPgOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok($co->load(speculative => 1), "delete foreign key object on save 2 - $db_type");

  # Do the save
  ok($o->save, "delete foreign key object on save 3 - $db_type");

  # Now it's deleted
  $co = MyPgOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete foreign key object on save 4 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MyPgObject->new(id   => 700,
                       name => 'Ham',
                       flag => 0);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyPgObject->new(id => 700);
  $o->load;

  # TEST: Delete, set on save, delete, save
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 1 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 2 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MyPgOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Set on save
  $o->other_obj_on_save(k1 => 44, k2 => 55, k3 => 66);

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 44 && $other_obj->k2 == 55 && $other_obj->k3 == 66,
     "delete 2 foreign key object on save 4 - $db_type");

  # ...and that the foreign object has not yet been saved
  $co = MyPgOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 5 - $db_type");

  # Delete again
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 6 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 7 - $db_type");

  # Confirm that the foreign objects have not been saved
  $co = MyPgOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 8 - $db_type");
  $co = MyPgOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 9 - $db_type");

  # RESET
  $o->delete;

  $o = MyPgObject->new(id   => 800,
                       name => 'Lee',
                       flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyPgObject->new(id => 800);
  $o->load;

  # TEST: Set & save, delete on save, set on save, delete on save, save
  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "delete 3 foreign key object on save 1 - $db_type");

  # Confirm that both foreign objects are in the db
  $co = MyPgOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 2 - $db_type");
  $co = MyPgOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to old value
  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);

  # Delete on save
  $o->del_other_obj_on_save;  

  # Save
  $o->save;

  # Confirm that both foreign objects have been deleted
  $co = MyPgOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 4 - $db_type");
  $co = MyPgOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MyPgObject->new(id   => 900,
                       name => 'Kai',
                       flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyPgObject->new(id => 900);
  $o->load;

  # TEST: Delete on save, set on save, delete on save, set to same one, save
  $o->del_other_obj_on_save;

  # Set on save
  ok($o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3), "delete 4 foreign key object on save 1 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to previous value
  $o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3);

  # Save
  $o->save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 1 && $other_obj->k2 == 2 && $other_obj->k3 == 3,
     "delete 4 foreign key object on save 2 - $db_type");

  # Confirm that the new foreign object is there and the old one is not
  $co = MyPgOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 4 foreign key object on save 3 - $db_type");
  $co = MyPgOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 4 foreign key object on save 4 - $db_type");

  # End foreign key method tests

  # Start "one to many" method tests

  #
  # "one to many" get_set_now
  #

  # SETUP
  $o = MyPgObject->new(id   => 111,
                       name => 'Boo',
                       flag => 1);

  MyPgOtherObject2->new(id => 1, name => 'one', pid => 900)->save;

  @o2s = 
  (
    1,
    MyPgOtherObject2->new(id => 2, name => 'two'),
    { id => 3, name => 'three', pid => 111 },
  );

  # Set before save, save, set
  eval { $o->other2_objs_now(@o2s) };
  ok($@, "set one to many now 1 - $db_type");

  $o->save;

  ok($o->other2_objs_now(@o2s), "set one to many now 2 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 3 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 4 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 5 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 6 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 7 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 8 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 9 - $db_type");

  my $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set one to many now 10 - $db_type");

  # Set to undef
  $o->other2_objs_now(undef);

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 11 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 12 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 13 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 14 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 15 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 16 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 17 - $db_type");

  # RESET
  $o = MyPgObject->new(id => 111)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MyPgOtherObject2->new(id => 1, name => 'one'),
    MyPgOtherObject2->new(id => 7, name => 'seven'),
  );

  ok($o->other2_objs_now(\@o2s), "set 2 one to many now 1 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 2 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 3 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 2, "set 2 one to many now 4 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 111, "set 2 one to many now 5 - $db_type");
  ok($o2s[1]->id == 1 && $o2s[1]->pid == 111, "set 2 one to many now 6 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 2, "set 2 one to many now 7 - $db_type");

  #
  # "one to many" get_set_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyPgObject->new(id   => 222,
                       name => 'Hap',
                       flag => 1);

  @o2s = 
  (
    MyPgOtherObject2->new(id => 5, name => 'five'),
    MyPgOtherObject2->new(id => 6, name => 'six'),
    MyPgOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 1 - $db_type");

  ok($o2s[0]->id == 5 && $o2s[0]->pid == 222, "set one to many on save 2 - $db_type");
  ok($o2s[1]->id == 6 && $o2s[1]->pid == 222, "set one to many on save 3 - $db_type");
  ok($o2s[2]->id == 7 && $o2s[2]->pid == 222, "set one to many on save 4 - $db_type");

  ok(!MyPgOtherObject2->new(id => 5)->load(speculative => 1), "set one to many on save 5 - $db_type");
  ok(!MyPgOtherObject2->new(id => 6)->load(speculative => 1), "set one to many on save 6 - $db_type");
  ok(!MyPgOtherObject2->new(id => 7)->load(speculative => 1), "set one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 222, "set one to many on save 9 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 222, "set one to many on save 10 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 222, "set one to many on save 11 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 5)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 12 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 6)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 13 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 14 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set one to many on save 15 - $db_type");

  # RESET
  $o = MyPgObject->new(id => 222)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MyPgOtherObject2->new(id => 7, name => 'seven'),
    MyPgOtherObject2->new(id => 12, name => 'one'),
  );

  ok($o->other2_objs_on_save(\@o2s), "set 2 one to many on save 1 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 2 - $db_type");

  ok(!MyPgOtherObject2->new(id => 12)->load(speculative => 1), "set 2 one to many on save 3 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 one to many on save 4 - $db_type");

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set 2 one to many on save 5 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 6 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 9 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 10 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 11 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 12 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 2, "set one to many on save 15 - $db_type");

  # Set to undef
  $o->other2_objs_on_save(undef);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 16 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 17 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 18 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 19 - $db_type");

  $o2 = MyPgOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 20 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;

  push(@o2s, MyPgOtherObject2->new(name => 'added'));

  $o->other2_objs_on_save(\@o2s);

  $o->save;

  my $to = MyPgObject->new(id => $o->id)->load;

  @o2s = $o->other2_objs_on_save;

  is_deeply([ 'seven', 'one', 'added' ], [ map { $_->name } @o2s ], "add one to many on save 1 - $db_type");


  #
  # "one to many" add_now
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyPgObject->new(id   => 333,
                       name => 'Zoom',
                       flag => 1);

  $o->save;

  @o2s = 
  (
    MyPgOtherObject2->new(id => 5, name => 'five'),
    MyPgOtherObject2->new(id => 6, name => 'six'),
    MyPgOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_now(@o2s);

  # RESET
  $o = MyPgObject->new(id   => 333,
                       name => 'Zoom',
                       flag => 1);

  # Add, no args
  @o2s = ();
  ok($o->add_other2_objs_now(@o2s) == 0, "add one to many now 1 - $db_type");

  # Add before load/save
  @o2s = 
  (
    MyPgOtherObject2->new(id => 8, name => 'eight'),
  );

  eval { $o->add_other2_objs_now(@o2s) };

  ok($@, "add one to many now 2 - $db_type");

  # Add
  $o->load;

  my @oret = $o->add_other2_objs_now(@o2s);
  is(scalar @oret, scalar @o2s && $oret[0] eq $o2s[0] && 
     $oret[0]->isa('MyPgOtherObject2'), "add one to many now count - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 4, "add one to many now 3 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 333, "add one to many now 4 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 333, "add one to many now 5 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 333, "add one to many now 6 - $db_type");
  ok($o2s[3]->id == 8 && $o2s[3]->pid == 333, "add one to many now 7 - $db_type");

  ok(MyPgOtherObject2->new(id => 6)->load(speculative => 1), "add one to many now 8 - $db_type");
  ok(MyPgOtherObject2->new(id => 7)->load(speculative => 1), "add one to many now 9 - $db_type");
  ok(MyPgOtherObject2->new(id => 5)->load(speculative => 1), "add one to many now 10 - $db_type");
  ok(MyPgOtherObject2->new(id => 8)->load(speculative => 1), "add one to many now 11 - $db_type");

  #
  # "one to many" add_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyPgObject->new(id   => 444,
                       name => 'Blargh',
                       flag => 1);

  # Set on save, add on save, save
  @o2s = 
  (
    MyPgOtherObject2->new(id => 10, name => 'ten'),
  );

  # Set on save
  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs;
  ok(@o2s == 1, "add one to many on save 1 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 2 - $db_type");
  ok(!MyPgOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 3 - $db_type");

  @o2s = 
  (
    MyPgOtherObject2->new(id => 9, name => 'nine'),
  );

  # Add on save
  ok($o->add_other2_objs(@o2s), "add one to many on save 4 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 5 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 6 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[0]->pid == 444, "add one to many on save 7 - $db_type");

  ok(!MyPgOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 8 - $db_type");
  ok(!MyPgOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 9 - $db_type");

  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 10 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 11 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 12 - $db_type");

  ok(MyPgOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 13 - $db_type");
  ok(MyPgOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 14 - $db_type");

  # RESET
  $o = MyPgObject->new(id   => 444,
                       name => 'Blargh',
                       flag => 1);

  $o->load;

  # Add on save, save
  @o2s = 
  (
    MyPgOtherObject2->new(id => 11, name => 'eleven'),
  );

  # Add on save
  ok($o->add_other2_objs(\@o2s), "add one to many on save 15 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 16 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 17 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 18 - $db_type");

  ok(MyPgOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 19 - $db_type");
  ok(MyPgOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 20 - $db_type");
  ok(!MyPgOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 21 - $db_type");

  # Save
  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 3, "add one to many on save 22 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 23 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 24 - $db_type");
  ok($o2s[2]->id == 11 && $o2s[2]->pid == 444, "add one to many on save 25 - $db_type");

  ok(MyPgOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 26 - $db_type");
  ok(MyPgOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 27 - $db_type");
  ok(MyPgOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 28 - $db_type");

  # End "one to many" method tests

  # Start "load with ..." tests

  ok($o = MyPgObject->new(id => 444)->load(with => [ qw(other_obj other2_objs colors) ]),
     "load with 1 - $db_type");

  ok($o->{'other2_objs'} && $o->{'other2_objs'}[1]->name eq 'nine',
     "load with 2 - $db_type");

  $o = MyPgObject->new(id => 999);

  ok(!$o->load(with => [ qw(other_obj other2_objs colors) ], speculative => 1),
     "load with 3 - $db_type");

  $o = MyPgObject->new(id => 222);

  ok($o->load(with => 'colors'), "load with 4 - $db_type");

  # End "load with ..." tests

  # Start "many to many" tests

  #
  # "many to many" get_set_now
  #

  # SETUP

  $o = MyPgObject->new(id   => 30,
                       name => 'Color',
                       flag => 1);

  # Set
  @colors =
  (
    1, # red
    MyPgColor->new(id => 3), # blue
    { id => 5, name => 'orange' },
  );

  #MyPgColor->new(id => 2), # green
  #MyPgColor->new(id => 4), # pink

  # Set before save, save, set
  eval { $o->colors_now(@colors) };
  ok($@, "set many to many now 1 - $db_type");

  $o->save;

  ok($o->colors_now(@colors), "set many to many now 2 - $db_type");

  @colors = $o->colors_now;
  ok(@colors == 3, "set many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "set many to many now 4 - $db_type");
  ok($colors[1]->id == 5, "set many to many now 5 - $db_type");
  ok($colors[2]->id == 1, "set many to many now 6 - $db_type");

  $color = MyPgColor->new(id => 5);
  ok($color->load(speculative => 1), "set many to many now 7 - $db_type");

  ok(MyPgColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set many to many now 8 - $db_type");
  ok(MyPgColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set many to many now 9 - $db_type");
  ok(MyPgColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set many to many now 11 - $db_type");

  # Set to undef
  $o->colors_now(undef);

  @colors = $o->colors_now;
  ok(@colors == 3, "set 2 many to many now 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many now 2 - $db_type");
  ok($colors[1]->id == 5, "set 2 many to many now 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many now 4 - $db_type");

  $color = MyPgColor->new(id => 5);
  ok($color->load(speculative => 1), "set 2 many to many now 5 - $db_type");

  $color = MyPgColor->new(id => 3);
  ok($color->load(speculative => 1), "set 2 many to many now 6 - $db_type");

  $color = MyPgColor->new(id => 1);
  ok($color->load(speculative => 1), "set 2 many to many now 7 - $db_type");

  ok(MyPgColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set 2 many to many now 8 - $db_type");
  ok(MyPgColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set 2 many to many now 9 - $db_type");
  ok(MyPgColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set 2 many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 many to many now 11 - $db_type");

  #
  # "many to many" get_set_on_save
  #

  # SETUP
  $o = MyPgObject->new(id   => 40,
                       name => 'Cool',
                       flag => 1);

  # Set
  @colors =
  (
    MyPgColor->new(id => 1), # red
    3, # blue
    { id => 6, name => 'ochre' },
  );

  #MyPgColor->new(id => 2), # green
  #MyPgColor->new(id => 4), # pink

  $o->colors_on_save(@colors);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 1 - $db_type");

  ok($colors[0]->id == 1, "set many to many on save 2 - $db_type");
  ok($colors[1]->id == 3, "set many to many on save 3 - $db_type");
  ok($colors[2]->id == 6, "set many to many on save 4 - $db_type");

  ok(MyPgColor->new(id => 1)->load(speculative => 1), "set many to many on save 5 - $db_type");
  ok(MyPgColor->new(id => 3)->load(speculative => 1), "set many to many on save 6 - $db_type");
  ok(!MyPgColor->new(id => 6)->load(speculative => 1), "set many to many on save 7 - $db_type");

  ok(!MyPgColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set many to many on save 8 - $db_type");
  ok(!MyPgColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set many to many on save 9 - $db_type");
  ok(!MyPgColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set many to many on save 10 - $db_type");

  $o->save;

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 11 - $db_type");

  ok($colors[0]->id == 3, "set many to many on save 12 - $db_type");
  ok($colors[1]->id == 6, "set many to many on save 13 - $db_type");
  ok($colors[2]->id == 1, "set many to many on save 14 - $db_type");

  ok(MyPgColor->new(id => 1)->load(speculative => 1), "set many to many on save 15 - $db_type");
  ok(MyPgColor->new(id => 3)->load(speculative => 1), "set many to many on save 16 - $db_type");
  ok(MyPgColor->new(id => 6)->load(speculative => 1), "set many to many on save 17 - $db_type");

  ok(MyPgColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 18 - $db_type");
  ok(MyPgColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 19 - $db_type");
  ok(MyPgColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 20 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set many to many on save 21 - $db_type");

  # RESET
  $o = MyPgObject->new(id => 40)->load;

  # Set to undef
  $o->colors_on_save(undef);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set 2 many to many on save 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 6, "set 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many on save 4 - $db_type");

  ok(MyPgColor->new(id => 1)->load(speculative => 1), "set 2 many to many on save 5 - $db_type");
  ok(MyPgColor->new(id => 3)->load(speculative => 1), "set 2 many to many on save 6 - $db_type");
  ok(MyPgColor->new(id => 6)->load(speculative => 1), "set 2 many to many on save 7 - $db_type");

  ok(MyPgColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 8 - $db_type");
  ok(MyPgColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 9 - $db_type");
  ok(MyPgColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 10 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 many to many on save 11 - $db_type");

  # Tests for SQL efficiency of __check_and_merge
  # $DB::single = 1;
  #   $o->save(changes_only => 1);
  #   $o->colors_on_save({ id => 2 });
  # $Rose::DB::Object::Manager::Debug = 1;
  # $Rose::DB::Object::Debug = 1;
  #   $o->save(changes_only => 1);
  # exit;

  $o->colors([]);
  $o->save(changes_only => 1);

  $o->colors_on_save({ id => 1, name => 'redx' }, { id => 3 });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'redx',
     "colors merge 1 - $db_type");

  $o->colors_on_save({ id => 2 }, { id => 3, name => 'bluex' });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'bluex' && $colors->[1]->name eq 'green',
     "colors merge 2 - $db_type");

  #
  # "many to many" add_now
  #

  # SETUP
  $o = MyPgObject->new(id   => 50,
                       name => 'Blat',
                       flag => 1);

  $o->delete;

  @colors =
  (
    MyPgColor->new(id => 1), # red
    MyPgColor->new(id => 3), # blue  
  );

  #MyPgColor->new(id => 4), # pink

  $o->colors_on_save(\@colors);
  $o->save;

  $o = MyPgObject->new(id   => 50,
                       name => 'Blat',
                       flag => 1);
  # Add, no args
  @colors = ();
  ok($o->add_colors(@colors) == 0, "add many to many now 1 - $db_type");

  # Add before load/save
  @colors = 
  (
    MyPgColor->new(id => 7, name => 'puce'),
    MyPgColor->new(id => 2), # green
  );

  eval { $o->add_colors(@colors) };

  ok($@, "add many to many now 2 - $db_type");

  # Add
  $o->load;

  $o->add_colors(@colors);

  @colors = $o->colors;
  ok(@colors == 4, "add many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "add many to many now 4 - $db_type");
  ok($colors[1]->id == 2, "add many to many now 5 - $db_type");
  ok($colors[2]->id == 7, "add many to many now 6 - $db_type");
  ok($colors[3]->id == 1, "add many to many now 7 - $db_type");

  ok(MyPgColor->new(id => 3)->load(speculative => 1), "add many to many now 8 - $db_type");
  ok(MyPgColor->new(id => 2)->load(speculative => 1), "add many to many now 9 - $db_type");
  ok(MyPgColor->new(id => 7)->load(speculative => 1), "add many to many now 10 - $db_type");
  ok(MyPgColor->new(id => 1)->load(speculative => 1), "add many to many now 11 - $db_type");

  ok(MyPgColorMap->new(obj_id => 50, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 12 - $db_type");
  ok(MyPgColorMap->new(obj_id => 50, color_id => 2)->load(speculative => 1),
     "set 2 many to many on save 13 - $db_type");
  ok(MyPgColorMap->new(obj_id => 50, color_id => 7)->load(speculative => 1),
     "set 2 many to many on save 14 - $db_type");
  ok(MyPgColorMap->new(obj_id => 50, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 15 - $db_type");

  #
  # "many to many" add_on_save
  #

  # SETUP
  $o = MyPgObject->new(id   => 60,
                       name => 'Cretch',
                       flag => 1);

  $o->delete;

  # Set on save, add on save, save
  @colors = 
  (
    MyPgColor->new(id => 1), # red
    MyPgColor->new(id => 2), # green
  );

  # Set on save
  $o->colors_on_save(@colors);

  @colors = 
  (
    MyPgColor->new(id => 7), # puce
    MyPgColor->new(id => 8, name => 'tan'),
  );

  # Add on save
  my $num = $o->add_colors_on_save(@colors);
  is($num, scalar @colors, "add many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 2 - $db_type");

  ok($colors[0]->id == 1, "add many to many on save 3 - $db_type");
  ok($colors[1]->id == 2, "add many to many on save 4 - $db_type");
  ok($colors[2]->id == 7, "add many to many on save 5 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 6 - $db_type");

  ok(MyPgColor->new(id => 1)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MyPgColor->new(id => 2)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MyPgColor->new(id => 7)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(!MyPgColor->new(id => 8)->load(speculative => 1), "add many to many on save 10 - $db_type");

  ok(!MyPgColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "set many to many on save 11 - $db_type");
  ok(!MyPgColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "set many to many on save 12 - $db_type");
  ok(!MyPgColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "set many to many on save 13 - $db_type");
  ok(!MyPgColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "set many to many on save 14 - $db_type");

  $o->save;

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 15 - $db_type");

  ok($colors[0]->id == 2, "add many to many on save 16 - $db_type");
  ok($colors[1]->id == 7, "add many to many on save 17 - $db_type");
  ok($colors[2]->id == 1, "add many to many on save 18 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 19 - $db_type");

  ok(MyPgColor->new(id => 2)->load(speculative => 1), "add many to many on save 20 - $db_type");
  ok(MyPgColor->new(id => 7)->load(speculative => 1), "add many to many on save 21 - $db_type");
  ok(MyPgColor->new(id => 1)->load(speculative => 1), "add many to many on save 22 - $db_type");
  ok(MyPgColor->new(id => 8)->load(speculative => 1), "add many to many on save 21 - $db_type");

  ok(MyPgColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add many to many on save 22 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add many to many on save 23 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add many to many on save 24 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add many to many on save 25 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 4, "add many to many on save 26 - $db_type");

  # RESET
  $o = MyPgObject->new(id   => 60,
                       name => 'Cretch',
                       flag => 1);

  $o->load(with => 'colors');

  # Add on save, save
  @colors = 
  (
    MyPgColor->new(id => 9, name => 'aqua'),
  );

  # Add on save
  ok($o->add_colors_on_save(@colors), "add 2 many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 16 - $db_type");

  ok($colors[0]->id == 2, "add 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 7, "add 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 1, "add 2 many to many on save 4 - $db_type");
  ok($colors[3]->id == 8, "add 2 many to many on save 5 - $db_type");
  ok($colors[4]->id == 9, "add 2 many to many on save 6 - $db_type");

  ok(MyPgColor->new(id => 2)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MyPgColor->new(id => 7)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MyPgColor->new(id => 1)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(MyPgColor->new(id => 8)->load(speculative => 1), "add many to many on save 10 - $db_type");
  ok(!MyPgColor->new(id => 9)->load(speculative => 1), "add many to many on save 11 - $db_type");

  ok(MyPgColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 12 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 13 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 14 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 15 - $db_type");
  ok(!MyPgColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 16 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 4, "add 2 many to many on save 17 - $db_type");

  # Save
  $o->save;

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 18 - $db_type");

  ok($colors[0]->id == 9, "add 2 many to many on save 19 - $db_type");
  ok($colors[1]->id == 2, "add 2 many to many on save 20 - $db_type");
  ok($colors[2]->id == 7, "add 2 many to many on save 21 - $db_type");
  ok($colors[3]->id == 1, "add 2 many to many on save 22 - $db_type");
  ok($colors[4]->id == 8, "add 2 many to many on save 23 - $db_type");

  ok(MyPgColor->new(id => 9)->load(speculative => 1), "add many to many on save 24 - $db_type");
  ok(MyPgColor->new(id => 2)->load(speculative => 1), "add many to many on save 25 - $db_type");
  ok(MyPgColor->new(id => 7)->load(speculative => 1), "add many to many on save 26 - $db_type");
  ok(MyPgColor->new(id => 1)->load(speculative => 1), "add many to many on save 27 - $db_type");
  ok(MyPgColor->new(id => 8)->load(speculative => 1), "add many to many on save 28 - $db_type");

  ok(MyPgColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 29 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 20 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 31 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 32 - $db_type");
  ok(MyPgColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 33 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 5, "add 2 many to many on save 34 - $db_type");

  # End "many to many" tests

  test_meta(MyPgOtherObject2->meta, 'MyPg', $db_type);
}

#
# MySQL
#

SKIP: foreach my $db_type ('mysql')
{
  skip("MySQL tests", 359)  unless($HAVE_MYSQL);

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

  my $o = MyMySQLObject->new(name => 'John');

  ok(ref $o && $o->isa('MyMySQLObject'), "new() 1 - $db_type");

  $o->flag2('true');
  $o->date_created('now');
  $o->last_modified($o->date_created);
  $o->save_col(22);

  ok($o->save, "save() 1 - $db_type");
  ok($o->load, "load() 1 - $db_type");

  my $o_x = MyMySQLObject->new(id => 99, name => 'John X', flag => 0);
  $o_x->save;

  my $o2 = MyMySQLObject->new(id => $o->id);

  ok(ref $o2 && $o2->isa('MyMySQLObject'), "new() 2 - $db_type");

  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");

  ok($o2->load, "load() 2 - $db_type");
  ok(!$o2->not_found, "not_found() 1 - $db_type");

  is($o2->name, $o->name, "load() verify 1 - $db_type");
  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
  is($o2->save_col, 22, "load() verify 7 (aliased column) - $db_type");
  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");

  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");

  $o2->name('John 2');
  $o2->start('5/24/2001');

  sleep(1); # keep the last modified dates from being the same

  $o2->last_modified('now');
  ok($o2->save, "save() 2 - $db_type");
  ok($o2->load, "load() 3 - $db_type");

  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");

  my $o3 = MyMySQLObject->new();

  my $db = $o3->db or die $o3->error;

  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");

  is($db->dbh, $o3->dbh, "dbh() - $db_type");

  my $o4 = MyMySQLObject->new(id => 999);
  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
  ok($o4->not_found, "not_found() 2 - $db_type");

  my $oo21 = MyMySQLOtherObject2->new(id => 1, name => 'one', pid => $o->id);
  ok($oo21->save, "other object 2 save() 1 - $db_type");

  my $oo22 = MyMySQLOtherObject2->new(id => 2, name => 'two', pid => $o->id);
  ok($oo22->save, "other object 2 save() 2 - $db_type");

  my $oo23 = MyMySQLOtherObject2->new(id => 3, name => 'three', pid => $o_x->id);
  ok($oo23->save, "other object 2 save() 3 - $db_type");

  # Begin filtered collection tests

  my $x = MyMySQLObject->new(id => $o->id)->load;
  $x->other2_a_objs({ name => 'aoo' }, { name => 'abc' });

  $x->save;

  $x = MyMySQLObject->new(id => $o->id)->load;

  my $ao = $x->other2_a_objs;
  my $oo = $x->other2_objs;

  is(scalar @$ao, 2, "filtered one-to-many 1 - $db_type");
  is(join(',', map { $_->name } @$ao), 'abc,aoo', "filtered one-to-many 2 - $db_type");

  is(scalar @$oo, 4, "filtered one-to-many 3 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'abc,aoo,one,two', "filtered one-to-many 4 - $db_type");

  $x->other2_a_objs({ name => 'axx' });
  $x->save;

  $x = MyMySQLObject->new(id => $o->id)->load;

  $ao = $x->other2_a_objs;
  $oo = $x->other2_objs;

  is(scalar @$ao, 1, "filtered one-to-many 5 - $db_type");
  is(join(',', map { $_->name } @$ao), 'axx', "filtered one-to-many 6 - $db_type");

  is(scalar @$oo, 3, "filtered one-to-many 7 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'axx,one,two', "filtered one-to-many 8 - $db_type");

  $x->other2_a_objs([]);
  $x->save;

  # End filtered collection tests

  ok(!$o->has_loaded_related('other2_objs'), "has_loaded_related() 1 - $db_type");

  my $o2s = $o->other2_objs;

  ok($o->has_loaded_related('other2_objs'), "has_loaded_related() 2 - $db_type");

  ok(ref $o2s eq 'ARRAY' && @$o2s == 2 && 
     $o2s->[0]->name eq 'two' && $o2s->[1]->name eq 'one',
     'other objects 1');

  my @o2s = $o->other2_objs;

  ok(@o2s == 2 && $o2s[0]->name eq 'two' && $o2s[1]->name eq 'one',
     'other objects 2');

  my $color = MyMySQLColor->new(id => 1, name => 'red');
  ok($color->save, "save color 1 - $db_type");

  $color = MyMySQLColor->new(id => 2, name => 'green');
  ok($color->save, "save color 2 - $db_type");

  $color = MyMySQLColor->new(id => 3, name => 'blue');
  ok($color->save, "save color 3 - $db_type");

  $color = MyMySQLColor->new(id => 4, name => 'pink');
  ok($color->save, "save color 4 - $db_type");

  my $map1 = MyMySQLColorMap->new(obj_id => 1, color_id => 1);
  ok($map1->save, "save color map record 1 - $db_type");

  my $map2 = MyMySQLColorMap->new(obj_id => 1, color_id => 3);
  ok($map2->save, "save color map record 2 - $db_type");

  my $map3 = MyMySQLColorMap->new(obj_id => 99, color_id => 4);
  ok($map3->save, "save color map record 3 - $db_type");

  my $colors = $o->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "colors 1 - $db_type");

  $colors = $o->find_colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "find colors 1 - $db_type");

  $colors = $o->find_colors([ name => { like => 'r%' } ]);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red',
     "find colors 2 - $db_type");

  $colors = $o->find_colors(query => [ name => { like => 'r%' } ], cache => 1);

  my $colors2 = $o->find_colors(from_cache => 1);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red' &&
     ref $colors2 eq 'ARRAY' && @$colors2 == 1 && $colors2->[0]->name eq 'red' &&
     $colors->[0] eq $colors2->[0],
     "find colors from cache - $db_type");

  my $count = $o->colors_count;

  is($count, 2, "count colors 1 - $db_type");

  $count = $o->colors_count([ name => { like => 'r%' } ]);

  is($count, 1, "count colors 2 - $db_type");

  my @colors = $o->colors;

  ok(@colors == 2 && $colors[0]->name eq 'blue' && $colors[1]->name eq 'red',
     "colors 2 - $db_type");

  $colors = $o_x->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'pink',
     "colors 3 - $db_type");

  @colors = $o_x->colors;

  ok(@colors == 1 && $colors[0]->name eq 'pink', "colors 4 - $db_type");

  $o = MyMySQLObject->new(id => 1)->load;

  $o->fk1(99);
  $o->fk2(99);
  $o->fk3(99);

  eval { $o->other_obj };
  ok($@, "fatal referential_integrity - $db_type");
  ok(!defined $o->other_obj_osoft, "ok referential_integrity 1 - $db_type");
  ok(!defined $o->other_obj_msoft, "ok referential_integrity 2 - $db_type");

  $o->fk1(1);
  $o->fk2(2);
  $o->fk3(3);
  $o->save;

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

  my $ret;

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $ret = $o->delete(cascade => 'null');
  };

  # Allow for exceptions in case some fancy new version of MySQL actually
  # tries preserve referential integrity.  Hey, you never know...
  ok($ret || $@, "delete cascade null 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyMySQLOtherObject2');

  is($count, 3, "delete cascade rollback confirm 2 - $db_type");

  $o = MyMySQLObject->new(id => 99)->load;
  $o->fk1(11);
  $o->fk2(12);
  $o->fk3(13);
  $o->save;

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $ret = $o->delete(cascade => 'null');
  };

  ok($ret || $@, "delete cascade null 2 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyMySQLColorMap');

  is($count, 3, "delete cascade confirm 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyMySQLOtherObject2');

  is($count, 3, "delete cascade confirm 2 - $db_type");

  eval { $o->meta->alias_column(nonesuch => 'foo') };
  ok($@, "alias_column() nonesuch - $db_type");

  # Start foreign key method tests

  #
  # Foreign key get_set_now
  #

  $o = MyMySQLObject->new(id   => 50,
                          name => 'Alex',
                          flag => 1);

  eval { $o->other_obj('abc') };
  ok($@, "set foreign key object: one arg - $db_type");

  eval { $o->other_obj(k1 => 1, k2 => 2, k3 => 3) };
  ok($@, "set foreign key object: no save - $db_type");

  $o->save;

  eval
  {
    local $o->db->dbh->{'PrintError'} = 0;
    $o->other_obj(k1 => 1, k2 => 2);
  };

  ok($@, "set foreign key object: too few keys - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 1 - $db_type");
  ok($o->fk1 == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 1 - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 2 - $db_type");
  ok($o->fk1 == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 2 - $db_type");

  #
  # Foreign key delete_now
  #

  ok($o->delete_other_obj, "delete foreign key object 1 - $db_type");

  ok(!defined $o->fk1 && !defined $o->fk2 && !defined $o->fk3, "delete foreign key object check keys 1 - $db_type");

  ok(!defined $o->other_obj && defined $o->error, "delete foreign key object confirm 1 - $db_type");

  ok(!defined $o->delete_other_obj, "delete foreign key object 2 - $db_type");

  #
  # Foreign key get_set_on_save
  #

  # TEST: Set, save
  $o = MyMySQLObject->new(id   => 100,
                          name => 'Bub',
                          flag => 1);

  ok($o->other_obj_on_save(k1 => 21, k2 => 22, k3 => 23), "set foreign key object on save 1 - $db_type");

  my $co = MyMySQLObject->new(id => 100);
  ok(!$co->load(speculative => 1), "set foreign key object on save 2 - $db_type");

  my $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 3 - $db_type");

  ok($o->save, "set foreign key object on save 4 - $db_type");

  $o = MyMySQLObject->new(id => 100);

  $o->load;

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 5 - $db_type");

  # TEST: Set, set to undef, save
  $o = MyMySQLObject->new(id   => 200,
                          name => 'Rose',
                          flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 6 - $db_type");

  $co = MyMySQLObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 7 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 8 - $db_type");

  $o->other_obj_on_save(undef);

  ok($o->save, "set foreign key object on save 9 - $db_type");

  $o = MyMySQLObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 10 - $db_type");

  $co = MyMySQLOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 11 - $db_type");

  $o->delete(cascade => 1);

  # TEST: Set, delete, save
  $o = MyMySQLObject->new(id   => 200,
                          name => 'Rose',
                          flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 12 - $db_type");

  $co = MyMySQLObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 13 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 14 - $db_type");

  ok($o->delete_other_obj, "set foreign key object on save 15 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok(!defined $other_obj && !defined $o->fk1 && !defined $o->fk2 && !defined $o->fk3,
     "set foreign key object on save 16 - $db_type");

  ok($o->save, "set foreign key object on save 17 - $db_type");

  $o = MyMySQLObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 18 - $db_type");

  $co = MyMySQLOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 19 - $db_type");

  $o->delete(cascade => 1);

  #
  # Foreign key delete_on_save
  #

  $o = MyMySQLObject->new(id   => 500,
                          name => 'Kip',
                          flag => 1);

  $o->other_obj_on_save(k1 => 7, k2 => 8, k3 => 9);
  $o->save;

  $o = MyMySQLObject->new(id => 500);
  $o->load;

  # TEST: Delete, save
  $o->del_other_obj_on_save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fk1 && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 1 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MyMySQLOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok($co->load(speculative => 1), "delete foreign key object on save 2 - $db_type");

  # Do the save
  ok($o->save, "delete foreign key object on save 3 - $db_type");

  # Now it's deleted
  $co = MyMySQLOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete foreign key object on save 4 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef
  ok(!defined $other_obj && !defined $o->fk1 && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MyMySQLObject->new(id   => 700,
                          name => 'Ham',
                          flag => 0);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyMySQLObject->new(id => 700);
  $o->load;

  # TEST: Delete, set on save, delete, save
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 1 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fk1 && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 2 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MyMySQLOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Set on save
  $o->other_obj_on_save(k1 => 44, k2 => 55, k3 => 66);

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 44 && $other_obj->k2 == 55 && $other_obj->k3 == 66,
     "delete 2 foreign key object on save 4 - $db_type");

  # ...and that the foreign object has not yet been saved
  $co = MyMySQLOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 5 - $db_type");

  # Delete again
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 6 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fk1 && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 7 - $db_type");

  # Confirm that the foreign objects have not been saved
  $co = MyMySQLOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 8 - $db_type");
  $co = MyMySQLOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 9 - $db_type");

  # RESET
  $o->delete;

  $o = MyMySQLObject->new(id   => 800,
                          name => 'Lee',
                          flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyMySQLObject->new(id => 800);
  $o->load;

  # TEST: Set & save, delete on save, set on save, delete on save, save
  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "delete 3 foreign key object on save 1 - $db_type");

  # Confirm that both foreign objects are in the db
  $co = MyMySQLOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 2 - $db_type");
  $co = MyMySQLOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to old value
  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);

  # Delete on save
  $o->del_other_obj_on_save;  

  # Save
  $o->save;

  # Confirm that both foreign objects have been deleted
  $co = MyMySQLOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 4 - $db_type");
  $co = MyMySQLOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MyMySQLObject->new(id   => 900,
                          name => 'Kai',
                          flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyMySQLObject->new(id => 900);
  $o->load;

  # TEST: Delete on save, set on save, delete on save, set to same one, save
  $o->del_other_obj_on_save;

  # Set on save
  ok($o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3), "delete 4 foreign key object on save 1 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to previous value
  $o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3);

  # Save
  $o->save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 1 && $other_obj->k2 == 2 && $other_obj->k3 == 3,
     "delete 4 foreign key object on save 2 - $db_type");

  # Confirm that the new foreign object is there and the old one is not
  $co = MyMySQLOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 4 foreign key object on save 3 - $db_type");
  $co = MyMySQLOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 4 foreign key object on save 4 - $db_type");

  # End foreign key method tests

  # Start "one to many" method tests

  #
  # "one to many" get_set_now
  #

  # SETUP
  $o = MyMySQLObject->new(id   => 111,
                          name => 'Boo',
                          flag => 1);

  @o2s = 
  (
    1,
    MyMySQLOtherObject2->new(id => 2, name => 'two'),
    { id => 3, name => 'three' },
  );

  # Set before save, save, set
  eval { $o->other2_objs_now(@o2s) };
  ok($@, "set one to many now 1 - $db_type");

  $o->save;

  ok($o->other2_objs_now(@o2s), "set one to many now 2 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 3 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 4 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 5 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 6 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 7 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 8 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 9 - $db_type");

  my $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set one to many now 10 - $db_type");

  # Set to undef
  $o->other2_objs_now(undef);

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 11 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 12 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 13 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 14 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 15 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 16 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 17 - $db_type");

  # RESET
  $o = MyMySQLObject->new(id => 111)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 1, name => 'one'),
    MyMySQLOtherObject2->new(id => 7, name => 'seven'),
  );

  ok($o->other2_objs_now(\@o2s), "set 2 one to many now 1 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 2 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 3 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 2, "set 2 one to many now 4 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 111, "set 2 one to many now 5 - $db_type");
  ok($o2s[1]->id == 1 && $o2s[1]->pid == 111, "set 2 one to many now 6 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 2, "set 2 one to many now 7 - $db_type");

  #
  # "one to many" get_set_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyMySQLObject->new(id   => 222,
                          name => 'Hap',
                          flag => 1);

  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 5, name => 'five'),
    MyMySQLOtherObject2->new(id => 6, name => 'six'),
    MyMySQLOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 1 - $db_type");

  ok($o2s[0]->id == 5 && $o2s[0]->pid == 222, "set one to many on save 2 - $db_type");
  ok($o2s[1]->id == 6 && $o2s[1]->pid == 222, "set one to many on save 3 - $db_type");
  ok($o2s[2]->id == 7 && $o2s[2]->pid == 222, "set one to many on save 4 - $db_type");

  ok(!MyMySQLOtherObject2->new(id => 5)->load(speculative => 1), "set one to many on save 5 - $db_type");
  ok(!MyMySQLOtherObject2->new(id => 6)->load(speculative => 1), "set one to many on save 6 - $db_type");
  ok(!MyMySQLOtherObject2->new(id => 7)->load(speculative => 1), "set one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 222, "set one to many on save 9 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 222, "set one to many on save 10 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 222, "set one to many on save 11 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 5)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 12 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 6)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 13 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 14 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set one to many on save 15 - $db_type");

  # RESET
  $o = MyMySQLObject->new(id => 222)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 7, name => 'seven'),
    MyMySQLOtherObject2->new(id => 12, name => 'one'),
  );

  ok($o->other2_objs_on_save(\@o2s), "set 2 one to many on save 1 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 2 - $db_type");

  ok(!MyMySQLOtherObject2->new(id => 12)->load(speculative => 1), "set 2 one to many on save 3 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 one to many on save 4 - $db_type");

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set 2 one to many on save 5 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 6 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 9 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 10 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 11 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 12 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 2, "set one to many on save 15 - $db_type");

  # Set to undef
  $o->other2_objs_on_save(undef);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 16 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 17 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 18 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 19 - $db_type");

  $o2 = MyMySQLOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 20 - $db_type");

  #
  # "one to many" add_now
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyMySQLObject->new(id   => 333,
                          name => 'Zoom',
                          flag => 1);

  $o->save;

  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 5, name => 'five'),
    MyMySQLOtherObject2->new(id => 6, name => 'six'),
    MyMySQLOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_now(@o2s);

  # RESET
  $o = MyMySQLObject->new(id   => 333,
                          name => 'Zoom',
                          flag => 1);

  # Add, no args
  @o2s = ();
  ok($o->add_other2_objs_now(@o2s) == 0, "add one to many now 1 - $db_type");

  # Add before load/save
  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 8, name => 'eight'),
  );

  eval { $o->add_other2_objs_now(@o2s) };

  ok($@, "add one to many now 2 - $db_type");

  # Add
  $o->load;

  my @oret = $o->add_other2_objs_now(@o2s);
  is(scalar @oret, scalar @o2s && $oret[0] eq $o2s[0] && 
     $oret[0]->isa('MyMySQLOtherObject2'), "add one to many now count - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 4, "add one to many now 3 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 333, "add one to many now 4 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 333, "add one to many now 5 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 333, "add one to many now 6 - $db_type");
  ok($o2s[3]->id == 8 && $o2s[3]->pid == 333, "add one to many now 7 - $db_type");

  ok(MyMySQLOtherObject2->new(id => 6)->load(speculative => 1), "add one to many now 8 - $db_type");
  ok(MyMySQLOtherObject2->new(id => 7)->load(speculative => 1), "add one to many now 9 - $db_type");
  ok(MyMySQLOtherObject2->new(id => 5)->load(speculative => 1), "add one to many now 10 - $db_type");
  ok(MyMySQLOtherObject2->new(id => 8)->load(speculative => 1), "add one to many now 11 - $db_type");

  #
  # "one to many" add_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyMySQLObject->new(id   => 444,
                          name => 'Blargh',
                          flag => 1);

  # Set on save, add on save, save
  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 10, name => 'ten'),
  );

  # Set on save
  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs;
  ok(@o2s == 1, "add one to many on save 1 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 2 - $db_type");
  ok(!MyMySQLOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 3 - $db_type");

  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 9, name => 'nine'),
  );

  # Add on save
  ok($o->add_other2_objs(@o2s), "add one to many on save 4 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 5 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 6 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[0]->pid == 444, "add one to many on save 7 - $db_type");

  ok(!MyMySQLOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 8 - $db_type");
  ok(!MyMySQLOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 9 - $db_type");

  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 10 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 11 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 12 - $db_type");

  ok(MyMySQLOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 13 - $db_type");
  ok(MyMySQLOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 14 - $db_type");

  # RESET
  $o = MyMySQLObject->new(id   => 444,
                          name => 'Blargh',
                          flag => 1);

  $o->load;

  # Add on save, save
  @o2s = 
  (
    MyMySQLOtherObject2->new(id => 11, name => 'eleven'),
  );

  # Add on save
  ok($o->add_other2_objs(\@o2s), "add one to many on save 15 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 16 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 17 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 18 - $db_type");

  ok(MyMySQLOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 19 - $db_type");
  ok(MyMySQLOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 20 - $db_type");
  ok(!MyMySQLOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 21 - $db_type");

  # Save
  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 3, "add one to many on save 22 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 23 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 24 - $db_type");
  ok($o2s[2]->id == 11 && $o2s[2]->pid == 444, "add one to many on save 25 - $db_type");

  ok(MyMySQLOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 26 - $db_type");
  ok(MyMySQLOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 27 - $db_type");
  ok(MyMySQLOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 28 - $db_type");

  # End "one to many" method tests

  # Start "load with ..." tests

  ok($o = MyMySQLObject->new(id => 444)->load(with => [ qw(other_obj other2_objs colors) ]),
     "load with 1 - $db_type");

  ok($o->{'other2_objs'} && $o->{'other2_objs'}[1]->name eq 'nine',
     "load with 2 - $db_type");

  $o = MyMySQLObject->new(id => 999);

  ok(!$o->load(with => [ qw(other_obj other2_objs colors) ], speculative => 1),
     "load with 3 - $db_type");

  $o = MyMySQLObject->new(id => 222);

  ok($o->load(with => 'colors'), "load with 4 - $db_type");

  # End "load with ..." tests

  # Start "many to many" tests

  #
  # "many to many" get_set_now
  #

  # SETUP

  $o = MyMySQLObject->new(id   => 30,
                          name => 'Color',
                          flag => 1);

  # Set
  @colors =
  (
    1, # red
    MyMySQLColor->new(id => 3), # blue
    { id => 5, name => 'orange' },
  );

  #MyMySQLColor->new(id => 2), # green
  #MyMySQLColor->new(id => 4), # pink

  # Set before save, save, set
  eval { $o->colors_now(@colors) };
  ok($@, "set many to many now 1 - $db_type");

  $o->save;

  ok($o->colors_now(@colors), "set many to many now 2 - $db_type");

  @colors = $o->colors_now;
  ok(@colors == 3, "set many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "set many to many now 4 - $db_type");
  ok($colors[1]->id == 5, "set many to many now 5 - $db_type");
  ok($colors[2]->id == 1, "set many to many now 6 - $db_type");

  $color = MyMySQLColor->new(id => 5);
  ok($color->load(speculative => 1), "set many to many now 7 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set many to many now 8 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set many to many now 9 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set many to many now 11 - $db_type");

  # Set to undef
  $o->colors_now(undef);

  @colors = $o->colors_now;
  ok(@colors == 3, "set 2 many to many now 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many now 2 - $db_type");
  ok($colors[1]->id == 5, "set 2 many to many now 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many now 4 - $db_type");

  $color = MyMySQLColor->new(id => 5);
  ok($color->load(speculative => 1), "set 2 many to many now 5 - $db_type");

  $color = MyMySQLColor->new(id => 3);
  ok($color->load(speculative => 1), "set 2 many to many now 6 - $db_type");

  $color = MyMySQLColor->new(id => 1);
  ok($color->load(speculative => 1), "set 2 many to many now 7 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set 2 many to many now 8 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set 2 many to many now 9 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set 2 many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 many to many now 11 - $db_type");

  #
  # "many to many" get_set_on_save
  #

  # SETUP
  $o = MyMySQLObject->new(id   => 40,
                          name => 'Cool',
                          flag => 1);

  # Set
  @colors =
  (
    MyMySQLColor->new(id => 1), # red
    3, # blue
    { id => 6, name => 'ochre' },
  );

  #MyMySQLColor->new(id => 2), # green
  #MyMySQLColor->new(id => 4), # pink

  $o->colors_on_save(@colors);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 1 - $db_type");

  ok($colors[0]->id == 1, "set many to many on save 2 - $db_type");
  ok($colors[1]->id == 3, "set many to many on save 3 - $db_type");
  ok($colors[2]->id == 6, "set many to many on save 4 - $db_type");

  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "set many to many on save 5 - $db_type");
  ok(MyMySQLColor->new(id => 3)->load(speculative => 1), "set many to many on save 6 - $db_type");
  ok(!MyMySQLColor->new(id => 6)->load(speculative => 1), "set many to many on save 7 - $db_type");

  ok(!MyMySQLColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set many to many on save 8 - $db_type");
  ok(!MyMySQLColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set many to many on save 9 - $db_type");
  ok(!MyMySQLColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set many to many on save 10 - $db_type");

  $o->save;

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 11 - $db_type");

  ok($colors[0]->id == 3, "set many to many on save 12 - $db_type");
  ok($colors[1]->id == 6, "set many to many on save 13 - $db_type");
  ok($colors[2]->id == 1, "set many to many on save 14 - $db_type");

  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "set many to many on save 15 - $db_type");
  ok(MyMySQLColor->new(id => 3)->load(speculative => 1), "set many to many on save 16 - $db_type");
  ok(MyMySQLColor->new(id => 6)->load(speculative => 1), "set many to many on save 17 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 18 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 19 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 20 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set many to many on save 21 - $db_type");

  # RESET
  $o = MyMySQLObject->new(id => 40)->load;

  # Set to undef
  $o->colors_on_save(undef);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set 2 many to many on save 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 6, "set 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many on save 4 - $db_type");

  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "set 2 many to many on save 5 - $db_type");
  ok(MyMySQLColor->new(id => 3)->load(speculative => 1), "set 2 many to many on save 6 - $db_type");
  ok(MyMySQLColor->new(id => 6)->load(speculative => 1), "set 2 many to many on save 7 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 8 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 9 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 10 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 many to many on save 11 - $db_type");

  $o->colors([]);
  $o->save(changes_only => 1);

  $o->colors_on_save({ id => 1, name => 'redx' }, { id => 3 });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'redx',
     "colors merge 1 - $db_type");

  $o->colors_on_save({ id => 2 }, { id => 3, name => 'bluex' });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'bluex' && $colors->[1]->name eq 'green',
     "colors merge 2 - $db_type");

  #
  # "many to many" add_now
  #

  # SETUP
  $o = MyMySQLObject->new(id   => 50,
                          name => 'Blat',
                          flag => 1);

  $o->delete;

  @colors =
  (
    MyMySQLColor->new(id => 1), # red
    MyMySQLColor->new(id => 3), # blue  
  );

  #MyMySQLColor->new(id => 4), # pink

  $o->colors_on_save(\@colors);
  $o->save;

  $o = MyMySQLObject->new(id   => 50,
                          name => 'Blat',
                          flag => 1);
  # Add, no args
  @colors = ();
  ok($o->add_colors(@colors) == 0, "add many to many now 1 - $db_type");

  # Add before load/save
  @colors = 
  (
    MyMySQLColor->new(id => 7, name => 'puce'),
    MyMySQLColor->new(id => 2), # green
  );

  eval { $o->add_colors(@colors) };

  ok($@, "add many to many now 2 - $db_type");

  # Add
  $o->load;

  $o->add_colors(@colors);

  @colors = $o->colors;
  ok(@colors == 4, "add many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "add many to many now 4 - $db_type");
  ok($colors[1]->id == 2, "add many to many now 5 - $db_type");
  ok($colors[2]->id == 7, "add many to many now 6 - $db_type");
  ok($colors[3]->id == 1, "add many to many now 7 - $db_type");

  ok(MyMySQLColor->new(id => 3)->load(speculative => 1), "add many to many now 8 - $db_type");
  ok(MyMySQLColor->new(id => 2)->load(speculative => 1), "add many to many now 9 - $db_type");
  ok(MyMySQLColor->new(id => 7)->load(speculative => 1), "add many to many now 10 - $db_type");
  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "add many to many now 11 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 50, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 12 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 50, color_id => 2)->load(speculative => 1),
     "set 2 many to many on save 13 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 50, color_id => 7)->load(speculative => 1),
     "set 2 many to many on save 14 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 50, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 15 - $db_type");

  #
  # "many to many" add_on_save
  #

  # SETUP
  $o = MyMySQLObject->new(id   => 60,
                          name => 'Cretch',
                          flag => 1);

  $o->delete;

  # Set on save, add on save, save
  @colors = 
  (
    MyMySQLColor->new(id => 1), # red
    MyMySQLColor->new(id => 2), # green
  );

  # Set on save
  $o->colors_on_save(@colors);

  @colors = 
  (
    MyMySQLColor->new(id => 7), # puce
    MyMySQLColor->new(id => 8, name => 'tan'),
  );

  # Add on save
  ok($o->add_colors_on_save(@colors), "add many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 2 - $db_type");

  ok($colors[0]->id == 1, "add many to many on save 3 - $db_type");
  ok($colors[1]->id == 2, "add many to many on save 4 - $db_type");
  ok($colors[2]->id == 7, "add many to many on save 5 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 6 - $db_type");

  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MyMySQLColor->new(id => 2)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MyMySQLColor->new(id => 7)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(!MyMySQLColor->new(id => 8)->load(speculative => 1), "add many to many on save 10 - $db_type");

  ok(!MyMySQLColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "set many to many on save 11 - $db_type");
  ok(!MyMySQLColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "set many to many on save 12 - $db_type");
  ok(!MyMySQLColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "set many to many on save 13 - $db_type");
  ok(!MyMySQLColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "set many to many on save 14 - $db_type");

  $o->save;

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 15 - $db_type");

  ok($colors[0]->id == 2, "add many to many on save 16 - $db_type");
  ok($colors[1]->id == 7, "add many to many on save 17 - $db_type");
  ok($colors[2]->id == 1, "add many to many on save 18 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 19 - $db_type");

  ok(MyMySQLColor->new(id => 2)->load(speculative => 1), "add many to many on save 20 - $db_type");
  ok(MyMySQLColor->new(id => 7)->load(speculative => 1), "add many to many on save 21 - $db_type");
  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "add many to many on save 22 - $db_type");
  ok(MyMySQLColor->new(id => 8)->load(speculative => 1), "add many to many on save 21 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add many to many on save 22 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add many to many on save 23 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add many to many on save 24 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add many to many on save 25 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 4, "add many to many on save 26 - $db_type");

  # RESET
  $o = MyMySQLObject->new(id   => 60,
                          name => 'Cretch',
                          flag => 1);

  $o->load(with => 'colors');

  # Add on save, save
  @colors = 
  (
    MyMySQLColor->new(id => 9, name => 'aqua'),
  );

  # Add on save
  ok($o->add_colors_on_save(@colors), "add 2 many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 16 - $db_type");

  ok($colors[0]->id == 2, "add 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 7, "add 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 1, "add 2 many to many on save 4 - $db_type");
  ok($colors[3]->id == 8, "add 2 many to many on save 5 - $db_type");
  ok($colors[4]->id == 9, "add 2 many to many on save 6 - $db_type");

  ok(MyMySQLColor->new(id => 2)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MyMySQLColor->new(id => 7)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(MyMySQLColor->new(id => 8)->load(speculative => 1), "add many to many on save 10 - $db_type");
  ok(!MyMySQLColor->new(id => 9)->load(speculative => 1), "add many to many on save 11 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 12 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 13 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 14 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 15 - $db_type");
  ok(!MyMySQLColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 16 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 4, "add 2 many to many on save 17 - $db_type");

  # Save
  $o->save;

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 18 - $db_type");

  ok($colors[0]->id == 9, "add 2 many to many on save 19 - $db_type");
  ok($colors[1]->id == 2, "add 2 many to many on save 20 - $db_type");
  ok($colors[2]->id == 7, "add 2 many to many on save 21 - $db_type");
  ok($colors[3]->id == 1, "add 2 many to many on save 22 - $db_type");
  ok($colors[4]->id == 8, "add 2 many to many on save 23 - $db_type");

  ok(MyMySQLColor->new(id => 9)->load(speculative => 1), "add many to many on save 24 - $db_type");
  ok(MyMySQLColor->new(id => 2)->load(speculative => 1), "add many to many on save 25 - $db_type");
  ok(MyMySQLColor->new(id => 7)->load(speculative => 1), "add many to many on save 26 - $db_type");
  ok(MyMySQLColor->new(id => 1)->load(speculative => 1), "add many to many on save 27 - $db_type");
  ok(MyMySQLColor->new(id => 8)->load(speculative => 1), "add many to many on save 28 - $db_type");

  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 29 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 20 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 31 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 32 - $db_type");
  ok(MyMySQLColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 33 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 5, "add 2 many to many on save 34 - $db_type");

  # End "many to many" tests

  # Start "one to one" cascaded delete tests

  #local $Rose::DB::Object::Debug = 1;
  #local $Rose::DB::Object::Manager::Debug = 1;
  $o = MyMySQLObject->new(name => '1to1bug',
                          fk1 => 10,
                          fk2 => 20,
                          fk3 => 30,
                          other_obj_otoo =>
                          {
                            name => '1to1bugfo',
                            k1 => 10,
                            k2 => 20,
                            k3 => 30,
                          });

  $o->save;

  $o = MyMySQLObject->new(id => $o->id)->load;

  ok(defined $o->other_obj_otoo, "delete(cascade => 1) one to one prep - $db_type");

  $o = MyMySQLObject->new(id => $o->id);
  $o->delete(cascade => 1);

  ok(!MyMySQLOtherObject->new(k1 => 10, k2 => 20, k3 => 30)->load(speculative => 1),
     "delete(cascade => 1) one to one delete - $db_type");

  # XXX: This relies on MySQL's creepy behavior of setting not-null
  # XXX: columns to 0 when they are set to NULL by a query.
  #
  # $o = MyMySQLObject->new(name => '1to1bug2',
  #                         fk1 => 10,
  #                         fk2 => 20,
  #                         fk3 => 30,
  #                         other_obj_otoo =>
  #                         {
  #                           name => '1to1bugfo2',
  #                           k1 => 10,
  #                           k2 => 20,
  #                           k3 => 30,
  #                         });
  # 
  # $o->save;
  # 
  # $o = MyMySQLObject->new(id => $o->id)->load;
  # 
  # ok(defined $o->other_obj_otoo, "delete(cascade => 1) one to one prep - $db_type");
  # 
  # $o = MyMySQLObject->new(id => $o->id);
  # $o->delete(cascade => 'null');
  # 
  # ok(MyMySQLOtherObject->new(k1 => 0, k2 => 0, k3 => 0)->load(speculative => 1),
  #    "delete(cascade => 1) one to one null - $db_type");

  # End "one to one" cascaded delete tests

  # Start fk hook-up tests

  $o2 = MyMySQLOtherObject2->new(name => 'B', pid => 11);
  $o2->save;

  $o = MyMySQLObject->new(name => 'John', id => 12);

  $o->add_other2_objs2($o2);
  $o2->name('John2');
  $o->save;

  $o2 = MyMySQLOtherObject2->new(id => $o2->id)->load;

  is($o2->pid, $o->id, "fk hook-up 1 - $db_type");
  is($o2->name, 'John2', "fk hook-up 2 - $db_type");

  # End fk hook-up tests
}

#
# Informix
#

SKIP: foreach my $db_type ('informix')
{
  skip("Informix tests", 378)  unless($HAVE_INFORMIX);

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

  my $o = MyInformixObject->new(name => 'John', id => 1);

  ok(ref $o && $o->isa('MyInformixObject'), "new() 1 - $db_type");

  $o->flag2('true');
  $o->date_created('now');
  $o->last_modified($o->date_created);
  $o->save_col(7);

  ok($o->save, "save() 1 - $db_type");
  ok($o->load, "load() 1 - $db_type");

  my $o_x = MyInformixObject->new(id => 99, name => 'John X', flag => 0);
  $o_x->save;

  my $o2 = MyInformixObject->new(id => $o->id);

  ok(ref $o2 && $o2->isa('MyInformixObject'), "new() 2 - $db_type");

  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");

  ok($o2->load, "load() 2 - $db_type");
  ok(!$o2->not_found, "not_found() 1 - $db_type");

  is($o2->name, $o->name, "load() verify 1 - $db_type");
  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
  is($o2->save_col, 7, "load() verify 7 (aliased column) - $db_type");
  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");

  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");

  $o2->name('John 2');
  $o2->start('5/24/2001');

  sleep(1); # keep the last modified dates from being the same

  $o2->last_modified('now');
  ok($o2->save, "save() 2 - $db_type");
  ok($o2->load, "load() 3 - $db_type");

  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");

  my $o3 = MyInformixObject->new();

  my $db = $o3->db or die $o3->error;

  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");

  is($db->dbh, $o3->dbh, "dbh() - $db_type");

  my $o4 = MyInformixObject->new(id => 999);
  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
  ok($o4->not_found, "not_found() 2 - $db_type");

  ok($o->load, "load() 4 - $db_type");

  my $o5 = MyInformixObject->new(id => $o->id);

  ok($o5->load, "load() 5 - $db_type");

  $o5->nums([ 4, 5, 6 ]);
  ok($o5->save, "save() 4 - $db_type");
  ok($o->load, "load() 6 - $db_type");

  is($o5->nums->[0], 4, "load() verify 10 (array value) - $db_type");
  is($o5->nums->[1], 5, "load() verify 11 (array value) - $db_type");
  is($o5->nums->[2], 6, "load() verify 12 (array value) - $db_type");

  my @a = $o5->nums;

  is($a[0], 4, "load() verify 13 (array value) - $db_type");
  is($a[1], 5, "load() verify 14 (array value) - $db_type");
  is($a[2], 6, "load() verify 15 (array value) - $db_type");
  is(@a, 3, "load() verify 16 (array value) - $db_type");

  my $oo1 = MyInformixOtherObject->new(k1 => 1, k2 => 2, k3 => 3, name => 'one');
  ok($oo1->save, "other object save() 1 - $db_type");

  my $oo2 = MyInformixOtherObject->new(k1 => 11, k2 => 12, k3 => 13, name => 'two');
  ok($oo2->save, "other object save() 2 - $db_type");

  is($o->other_obj, undef, "other_obj() 1 - $db_type");

  $o->fkone(99);
  $o->fk2(99);
  $o->fk3(99);

  eval { $o->other_obj };
  ok($@, "fatal referential_integrity - $db_type");
  ok(!defined $o->other_obj_osoft, "ok referential_integrity 1 - $db_type");
  ok(!defined $o->other_obj_msoft, "ok referential_integrity 2 - $db_type");

  $o->fkone(1);
  $o->fk2(2);
  $o->fk3(3);

  my $obj = $o->other_obj or warn "# ", $o->error, "\n";

  is(ref $obj, 'MyInformixOtherObject', "other_obj() 2 - $db_type");
  is($obj->name, 'one', "other_obj() 3 - $db_type");

  $o->other_obj(undef);
  $o->fkone(11);
  $o->fk2(12);
  $o->fk3(13);

  ok(!$o->has_loaded_related('other_obj'), "has_loaded_related() 1 - $db_type");

  $obj = $o->other_obj or warn "# ", $o->error, "\n";

  ok($o->has_loaded_related('other_obj'), "has_loaded_related() 2 - $db_type");

  is(ref $obj, 'MyInformixOtherObject', "other_obj() 4 - $db_type");
  is($obj->name, 'two', "other_obj() 5 - $db_type");

  my $oo21 = MyInformixOtherObject2->new(id => 1, name => 'one', pid => $o->id);
  ok($oo21->save, "other object 2 save() 1 - $db_type");

  my $oo22 = MyInformixOtherObject2->new(id => 2, name => 'two', pid => $o->id);
  ok($oo22->save, "other object 2 save() 2 - $db_type");

  my $oo23 = MyInformixOtherObject2->new(id => 3, name => 'three', pid => $o_x->id);
  ok($oo23->save, "other object 2 save() 3 - $db_type");

  # Begin filtered collection tests

  my $x = MyInformixObject->new(id => $o->id)->load;
  $x->other2_a_objs({ id => 100, name => 'aoo' }, { id => 101, name => 'abc' });

  $x->save;

  $x = MyInformixObject->new(id => $o->id)->load;

  my $ao = $x->other2_a_objs;
  my $oo = $x->other2_objs;

  is(scalar @$ao, 2, "filtered one-to-many 1 - $db_type");
  is(join(',', map { $_->name } @$ao), 'abc,aoo', "filtered one-to-many 2 - $db_type");

  is(scalar @$oo, 4, "filtered one-to-many 3 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'abc,aoo,one,two', "filtered one-to-many 4 - $db_type");

  $x->other2_a_objs({ id => 102, name => 'axx' });
  $x->save;

  $x = MyInformixObject->new(id => $o->id)->load;

  $ao = $x->other2_a_objs;
  $oo = $x->other2_objs;

  is(scalar @$ao, 1, "filtered one-to-many 5 - $db_type");
  is(join(',', map { $_->name } @$ao), 'axx', "filtered one-to-many 6 - $db_type");

  is(scalar @$oo, 3, "filtered one-to-many 7 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'axx,one,two', "filtered one-to-many 8 - $db_type");

  $x->other2_a_objs([]);
  $x->save;

  # End filtered collection tests

  ok(!$o->has_loaded_related('other2_objs'), "has_loaded_related() 3 - $db_type");

  my $o2s = $o->other2_objs;

  ok($o->has_loaded_related('other2_objs'), "has_loaded_related() 4 - $db_type");

  ok(ref $o2s eq 'ARRAY' && @$o2s == 2 && 
     $o2s->[0]->name eq 'two' && $o2s->[1]->name eq 'one',
     'other objects 1');

  my @o2s = $o->other2_objs;

  ok(@o2s == 2 && $o2s[0]->name eq 'two' && $o2s[1]->name eq 'one',
     'other objects 2');

  my $color = MyInformixColor->new(id => 1, name => 'red');
  ok($color->save, "save color 1 - $db_type");

  $color = MyInformixColor->new(id => 2, name => 'green');
  ok($color->save, "save color 2 - $db_type");

  $color = MyInformixColor->new(id => 3, name => 'blue');
  ok($color->save, "save color 3 - $db_type");

  $color = MyInformixColor->new(id => 4, name => 'pink');
  ok($color->save, "save color 4 - $db_type");

  my $map1 = MyInformixColorMap->new(obj_id => 1, color_id => 1);
  ok($map1->save, "save color map record 1 - $db_type");

  my $map2 = MyInformixColorMap->new(obj_id => 1, color_id => 3);
  ok($map2->save, "save color map record 2 - $db_type");

  my $map3 = MyInformixColorMap->new(obj_id => 99, color_id => 4);
  ok($map3->save, "save color map record 3 - $db_type");

  my $colors = $o->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "colors 1 - $db_type");

  $colors = $o->find_colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "find colors 1 - $db_type");

  $colors = $o->find_colors([ name => { like => 'r%' } ]);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red',
     "find colors 2 - $db_type");

  $colors = $o->find_colors(query => [ name => { like => 'r%' } ], cache => 1);

  my $colors2 = $o->find_colors(from_cache => 1);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red' &&
     ref $colors2 eq 'ARRAY' && @$colors2 == 1 && $colors2->[0]->name eq 'red' &&
     $colors->[0] eq $colors2->[0],
     "find colors from cache - $db_type");

  my $count = $o->colors_count;

  is($count, 2, "count colors 1 - $db_type");

  $count = $o->colors_count([ name => { like => 'r%' } ]);

  is($count, 1, "count colors 2 - $db_type");

  my @colors = $o->colors;

  ok(@colors == 2 && $colors[0]->name eq 'blue' && $colors[1]->name eq 'red',
     "colors 2 - $db_type");

  $colors = $o_x->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'pink',
     "colors 3 - $db_type");

  @colors = $o_x->colors;

  ok(@colors == 1 && $colors[0]->name eq 'pink', "colors 4 - $db_type");

  $o = MyInformixObject->new(id => 1)->load;
  $o->fkone(1);
  $o->fk2(2);
  $o->fk3(3);
  $o->save;

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

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $o->delete(cascade => 'null');
  };

  ok($@, "delete cascade null 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyInformixOtherObject');

  is($count, 2, "delete cascade rollback confirm 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyInformixOtherObject2');

  is($count, 3, "delete cascade rollback confirm 2 - $db_type");

  ok($o->delete(cascade => 'delete'), "delete cascade delete 1 - $db_type");

  $o = MyInformixObject->new(id => 99)->load;
  $o->fkone(11);
  $o->fk2(12);
  $o->fk3(13);
  $o->save;

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $o->delete(cascade => 'null');
  };

  ok($@, "delete cascade null 2 - $db_type");

  ok($o->delete(cascade => 'delete'), "delete cascade delete 2 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyInformixColorMap');

  is($count, 0, "delete cascade confirm 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyInformixOtherObject2');

  is($count, 0, "delete cascade confirm 2 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MyInformixOtherObject');

  is($count, 0, "delete cascade confirm 3 - $db_type");

  eval { $o->meta->alias_column(nonesuch => 'foo') };
  ok($@, "alias_column() nonesuch - $db_type");

  # Start foreign key method tests

  #
  # Foreign key get_set_now
  #

  $o = MyInformixObject->new(id   => 50,
                       name => 'Alex',
                       flag => 1);

  eval { $o->other_obj('abc') };
  ok($@, "set foreign key object: one arg - $db_type");

  eval { $o->other_obj(k1 => 1, k2 => 2, k3 => 3) };
  ok($@, "set foreign key object: no save - $db_type");

  $o->save;

  eval
  {
    local $o->db->dbh->{'PrintError'} = 0;
    $o->other_obj(k1 => 1, k2 => 2);
  };

  ok($@, "set foreign key object: too few keys - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 1 - $db_type");
  ok($o->fkone == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 1 - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 2 - $db_type");
  ok($o->fkone == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 2 - $db_type");

  #
  # Foreign key delete_now
  #

  ok($o->delete_other_obj, "delete foreign key object 1 - $db_type");

  ok(!defined $o->fkone && !defined $o->fk2 && !defined $o->fk3, "delete foreign key object check keys 1 - $db_type");

  ok(!defined $o->other_obj && defined $o->error, "delete foreign key object confirm 1 - $db_type");

  ok(!defined $o->delete_other_obj, "delete foreign key object 2 - $db_type");

  #
  # Foreign key get_set_on_save
  #

  # TEST: Set, save
  $o = MyInformixObject->new(id   => 100,
                       name => 'Bub',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 21, k2 => 22, k3 => 23), "set foreign key object on save 1 - $db_type");

  my $co = MyInformixObject->new(id => 100);
  ok(!$co->load(speculative => 1), "set foreign key object on save 2 - $db_type");

  my $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 3 - $db_type");

  ok($o->save, "set foreign key object on save 4 - $db_type");

  $o = MyInformixObject->new(id => 100);

  $o->load;

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 5 - $db_type");

  # TEST: Set, set to undef, save
  $o = MyInformixObject->new(id   => 200,
                       name => 'Rose',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 6 - $db_type");

  $co = MyInformixObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 7 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 8 - $db_type");

  $o->other_obj_on_save(undef);

  ok($o->save, "set foreign key object on save 9 - $db_type");

  $o = MyInformixObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 10 - $db_type");

  $co = MyInformixOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 11 - $db_type");

  $o->delete(cascade => 1);

  # TEST: Set, delete, save
  $o = MyInformixObject->new(id   => 200,
                       name => 'Rose',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 12 - $db_type");

  $co = MyInformixObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 13 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 14 - $db_type");

  ok($o->delete_other_obj, "set foreign key object on save 15 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "set foreign key object on save 16 - $db_type");

  ok($o->save, "set foreign key object on save 17 - $db_type");

  $o = MyInformixObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 18 - $db_type");

  $co = MyInformixOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 19 - $db_type");

  $o->delete(cascade => 1);

  #
  # Foreign key delete_on_save
  #

  $o = MyInformixObject->new(id   => 500,
                       name => 'Kip',
                       flag => 1);

  $o->other_obj_on_save(k1 => 7, k2 => 8, k3 => 9);
  $o->save;

  $o = MyInformixObject->new(id => 500);
  $o->load;

  # TEST: Delete, save
  $o->del_other_obj_on_save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 1 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MyInformixOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok($co->load(speculative => 1), "delete foreign key object on save 2 - $db_type");

  # Do the save
  ok($o->save, "delete foreign key object on save 3 - $db_type");

  # Now it's deleted
  $co = MyInformixOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete foreign key object on save 4 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MyInformixObject->new(id   => 700,
                       name => 'Ham',
                       flag => 0);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyInformixObject->new(id => 700);
  $o->load;

  # TEST: Delete, set on save, delete, save
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 1 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 2 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MyInformixOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Set on save
  $o->other_obj_on_save(k1 => 44, k2 => 55, k3 => 66);

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 44 && $other_obj->k2 == 55 && $other_obj->k3 == 66,
     "delete 2 foreign key object on save 4 - $db_type");

  # ...and that the foreign object has not yet been saved
  $co = MyInformixOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 5 - $db_type");

  # Delete again
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 6 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 7 - $db_type");

  # Confirm that the foreign objects have not been saved
  $co = MyInformixOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 8 - $db_type");
  $co = MyInformixOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 9 - $db_type");

  # RESET
  $o->delete;

  $o = MyInformixObject->new(id   => 800,
                       name => 'Lee',
                       flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyInformixObject->new(id => 800);
  $o->load;

  # TEST: Set & save, delete on save, set on save, delete on save, save
  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "delete 3 foreign key object on save 1 - $db_type");

  # Confirm that both foreign objects are in the db
  $co = MyInformixOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 2 - $db_type");
  $co = MyInformixOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to old value
  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);

  # Delete on save
  $o->del_other_obj_on_save;  

  # Save
  $o->save;

  # Confirm that both foreign objects have been deleted
  $co = MyInformixOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 4 - $db_type");
  $co = MyInformixOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MyInformixObject->new(id   => 900,
                       name => 'Kai',
                       flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MyInformixObject->new(id => 900);
  $o->load;

  # TEST: Delete on save, set on save, delete on save, set to same one, save
  $o->del_other_obj_on_save;

  # Set on save
  ok($o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3), "delete 4 foreign key object on save 1 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to previous value
  $o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3);

  # Save
  $o->save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 1 && $other_obj->k2 == 2 && $other_obj->k3 == 3,
     "delete 4 foreign key object on save 2 - $db_type");

  # Confirm that the new foreign object is there and the old one is not
  $co = MyInformixOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 4 foreign key object on save 3 - $db_type");
  $co = MyInformixOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 4 foreign key object on save 4 - $db_type");

  # End foreign key method tests

  # Start "one to many" method tests

  #
  # "one to many" get_set_now
  #

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

  # SETUP
  $o = MyInformixObject->new(id   => 111,
                       name => 'Boo',
                       flag => 1);

  @o2s = 
  (
    1,
    MyInformixOtherObject2->new(id => 2, name => 'two'),
    { id => 3, name => 'three' },
  );

  # Set before save, save, set
  eval { $o->other2_objs_now(@o2s) };
  ok($@, "set one to many now 1 - $db_type");

  $o->save;

  ok($o->other2_objs_now(@o2s), "set one to many now 2 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 3 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 4 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 5 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 6 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 7 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 8 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 9 - $db_type");

  my $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set one to many now 10 - $db_type");

  # Set to undef
  $o->other2_objs_now(undef);

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 11 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 12 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 13 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 14 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 15 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 16 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 17 - $db_type");

  # RESET
  $o = MyInformixObject->new(id => 111)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MyInformixOtherObject2->new(id => 1, name => 'one'),
    MyInformixOtherObject2->new(id => 7, name => 'seven'),
  );

  ok($o->other2_objs_now(\@o2s), "set 2 one to many now 1 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 2 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 3 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 2, "set 2 one to many now 4 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 111, "set 2 one to many now 5 - $db_type");
  ok($o2s[1]->id == 1 && $o2s[1]->pid == 111, "set 2 one to many now 6 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 2, "set 2 one to many now 7 - $db_type");

  #
  # "one to many" get_set_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyInformixObject->new(id   => 222,
                       name => 'Hap',
                       flag => 1);

  @o2s = 
  (
    MyInformixOtherObject2->new(id => 5, name => 'five'),
    MyInformixOtherObject2->new(id => 6, name => 'six'),
    MyInformixOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 1 - $db_type");

  ok($o2s[0]->id == 5 && $o2s[0]->pid == 222, "set one to many on save 2 - $db_type");
  ok($o2s[1]->id == 6 && $o2s[1]->pid == 222, "set one to many on save 3 - $db_type");
  ok($o2s[2]->id == 7 && $o2s[2]->pid == 222, "set one to many on save 4 - $db_type");

  ok(!MyInformixOtherObject2->new(id => 5)->load(speculative => 1), "set one to many on save 5 - $db_type");
  ok(!MyInformixOtherObject2->new(id => 6)->load(speculative => 1), "set one to many on save 6 - $db_type");
  ok(!MyInformixOtherObject2->new(id => 7)->load(speculative => 1), "set one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 222, "set one to many on save 9 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 222, "set one to many on save 10 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 222, "set one to many on save 11 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 5)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 12 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 6)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 13 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 14 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set one to many on save 15 - $db_type");

  # RESET
  $o = MyInformixObject->new(id => 222)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MyInformixOtherObject2->new(id => 7, name => 'seven'),
    MyInformixOtherObject2->new(id => 12, name => 'one'),
  );

  ok($o->other2_objs_on_save(\@o2s), "set 2 one to many on save 1 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 2 - $db_type");

  ok(!MyInformixOtherObject2->new(id => 12)->load(speculative => 1), "set 2 one to many on save 3 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 one to many on save 4 - $db_type");

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set 2 one to many on save 5 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 6 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 9 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 10 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 11 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 12 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 2, "set one to many on save 15 - $db_type");

  # Set to undef
  $o->other2_objs_on_save(undef);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 16 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 17 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 18 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 19 - $db_type");

  $o2 = MyInformixOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 20 - $db_type");

  #
  # "one to many" add_now
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyInformixObject->new(id   => 333,
                       name => 'Zoom',
                       flag => 1);

  $o->save;

  @o2s = 
  (
    MyInformixOtherObject2->new(id => 5, name => 'five'),
    MyInformixOtherObject2->new(id => 6, name => 'six'),
    MyInformixOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_now(@o2s);

  # RESET
  $o = MyInformixObject->new(id   => 333,
                       name => 'Zoom',
                       flag => 1);

  # Add, no args
  @o2s = ();
  ok($o->add_other2_objs_now(@o2s) == 0, "add one to many now 1 - $db_type");

  # Add before load/save
  @o2s = 
  (
    MyInformixOtherObject2->new(id => 8, name => 'eight'),
  );

  eval { $o->add_other2_objs_now(@o2s) };

  ok($@, "add one to many now 2 - $db_type");

  # Add
  $o->load;

  $o->add_other2_objs_now(@o2s);

  @o2s = $o->other2_objs;
  ok(@o2s == 4, "add one to many now 3 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 333, "add one to many now 4 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 333, "add one to many now 5 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 333, "add one to many now 6 - $db_type");
  ok($o2s[3]->id == 8 && $o2s[3]->pid == 333, "add one to many now 7 - $db_type");

  ok(MyInformixOtherObject2->new(id => 6)->load(speculative => 1), "add one to many now 8 - $db_type");
  ok(MyInformixOtherObject2->new(id => 7)->load(speculative => 1), "add one to many now 9 - $db_type");
  ok(MyInformixOtherObject2->new(id => 5)->load(speculative => 1), "add one to many now 10 - $db_type");
  ok(MyInformixOtherObject2->new(id => 8)->load(speculative => 1), "add one to many now 11 - $db_type");

  #
  # "one to many" add_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MyInformixObject->new(id   => 444,
                       name => 'Blargh',
                       flag => 1);

  # Set on save, add on save, save
  @o2s = 
  (
    MyInformixOtherObject2->new(id => 10, name => 'ten'),
  );

  # Set on save
  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs;
  ok(@o2s == 1, "add one to many on save 1 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 2 - $db_type");
  ok(!MyInformixOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 3 - $db_type");

  @o2s = 
  (
    MyInformixOtherObject2->new(id => 9, name => 'nine'),
  );

  # Add on save
  ok($o->add_other2_objs(@o2s), "add one to many on save 4 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 5 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 6 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[0]->pid == 444, "add one to many on save 7 - $db_type");

  ok(!MyInformixOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 8 - $db_type");
  ok(!MyInformixOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 9 - $db_type");

  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 10 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 11 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 12 - $db_type");

  ok(MyInformixOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 13 - $db_type");
  ok(MyInformixOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 14 - $db_type");

  # RESET
  $o = MyInformixObject->new(id   => 444,
                       name => 'Blargh',
                       flag => 1);

  $o->load;

  # Add on save, save
  @o2s = 
  (
    MyInformixOtherObject2->new(id => 11, name => 'eleven'),
  );

  # Add on save
  ok($o->add_other2_objs(\@o2s), "add one to many on save 15 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 16 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 17 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 18 - $db_type");

  ok(MyInformixOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 19 - $db_type");
  ok(MyInformixOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 20 - $db_type");
  ok(!MyInformixOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 21 - $db_type");

  # Save
  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 3, "add one to many on save 22 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 23 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 24 - $db_type");
  ok($o2s[2]->id == 11 && $o2s[2]->pid == 444, "add one to many on save 25 - $db_type");

  ok(MyInformixOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 26 - $db_type");
  ok(MyInformixOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 27 - $db_type");
  ok(MyInformixOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 28 - $db_type");

  # End "one to many" method tests

  # Start "load with ..." tests

  ok($o = MyInformixObject->new(id => 444)->load(with => [ qw(other_obj other2_objs colors) ]),
     "load with 1 - $db_type");

  ok($o->{'other2_objs'} && $o->{'other2_objs'}[1]->name eq 'nine',
     "load with 2 - $db_type");

  $o = MyInformixObject->new(id => 999);

  ok(!$o->load(with => [ qw(other_obj other2_objs colors) ], speculative => 1),
     "load with 3 - $db_type");

  $o = MyInformixObject->new(id => 222);

  ok($o->load(with => 'colors'), "load with 4 - $db_type");

  # End "load with ..." tests

  # Start "many to many" tests

  #
  # "many to many" get_set_now
  #

  # SETUP

  $o = MyInformixObject->new(id   => 30,
                             name => 'Color',
                             flag => 1);

  # Set
  @colors =
  (
    1, # red
    MyInformixColor->new(id => 3), # blue
    { id => 5, name => 'orange' },
  );

  #MyInformixColor->new(id => 2), # green
  #MyInformixColor->new(id => 4), # pink

  # Set before save, save, set
  eval { $o->colors_now(@colors) };
  ok($@, "set many to many now 1 - $db_type");

  $o->save;

  ok($o->colors_now(@colors), "set many to many now 2 - $db_type");

  @colors = $o->colors_now;
  ok(@colors == 3, "set many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "set many to many now 4 - $db_type");
  ok($colors[1]->id == 5, "set many to many now 5 - $db_type");
  ok($colors[2]->id == 1, "set many to many now 6 - $db_type");

  $color = MyInformixColor->new(id => 5);
  ok($color->load(speculative => 1), "set many to many now 7 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set many to many now 8 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set many to many now 9 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set many to many now 11 - $db_type");

  # Set to undef
  $o->colors_now(undef);

  @colors = $o->colors_now;
  ok(@colors == 3, "set 2 many to many now 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many now 2 - $db_type");
  ok($colors[1]->id == 5, "set 2 many to many now 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many now 4 - $db_type");

  $color = MyInformixColor->new(id => 5);
  ok($color->load(speculative => 1), "set 2 many to many now 5 - $db_type");

  $color = MyInformixColor->new(id => 3);
  ok($color->load(speculative => 1), "set 2 many to many now 6 - $db_type");

  $color = MyInformixColor->new(id => 1);
  ok($color->load(speculative => 1), "set 2 many to many now 7 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set 2 many to many now 8 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set 2 many to many now 9 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set 2 many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 many to many now 11 - $db_type");

  #
  # "many to many" get_set_on_save
  #

  # SETUP
  $o = MyInformixObject->new(id   => 40,
                             name => 'Cool',
                             flag => 1);

  # Set
  @colors =
  (
    MyInformixColor->new(id => 1), # red
    3, # blue
    { id => 6, name => 'ochre' },
  );

  #MyInformixColor->new(id => 2), # green
  #MyInformixColor->new(id => 4), # pink

  $o->colors_on_save(@colors);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 1 - $db_type");

  ok($colors[0]->id == 1, "set many to many on save 2 - $db_type");
  ok($colors[1]->id == 3, "set many to many on save 3 - $db_type");
  ok($colors[2]->id == 6, "set many to many on save 4 - $db_type");

  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "set many to many on save 5 - $db_type");
  ok(MyInformixColor->new(id => 3)->load(speculative => 1), "set many to many on save 6 - $db_type");
  ok(!MyInformixColor->new(id => 6)->load(speculative => 1), "set many to many on save 7 - $db_type");

  ok(!MyInformixColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set many to many on save 8 - $db_type");
  ok(!MyInformixColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set many to many on save 9 - $db_type");
  ok(!MyInformixColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set many to many on save 10 - $db_type");

  $o->save;

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 11 - $db_type");

  ok($colors[0]->id == 3, "set many to many on save 12 - $db_type");
  ok($colors[1]->id == 6, "set many to many on save 13 - $db_type");
  ok($colors[2]->id == 1, "set many to many on save 14 - $db_type");

  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "set many to many on save 15 - $db_type");
  ok(MyInformixColor->new(id => 3)->load(speculative => 1), "set many to many on save 16 - $db_type");
  ok(MyInformixColor->new(id => 6)->load(speculative => 1), "set many to many on save 17 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 18 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 19 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 20 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set many to many on save 21 - $db_type");

  # RESET
  $o = MyInformixObject->new(id => 40)->load;

  # Set to undef
  $o->colors_on_save(undef);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set 2 many to many on save 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 6, "set 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many on save 4 - $db_type");

  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "set 2 many to many on save 5 - $db_type");
  ok(MyInformixColor->new(id => 3)->load(speculative => 1), "set 2 many to many on save 6 - $db_type");
  ok(MyInformixColor->new(id => 6)->load(speculative => 1), "set 2 many to many on save 7 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 8 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 9 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 10 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 3, "set 2 many to many on save 11 - $db_type");

  $o->colors([]);
  $o->save(changes_only => 1);

  $o->colors_on_save({ id => 1, name => 'redx' }, { id => 3 });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'redx',
     "colors merge 1 - $db_type");

  $o->colors_on_save({ id => 2 }, { id => 3, name => 'bluex' });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'bluex' && $colors->[1]->name eq 'green',
     "colors merge 2 - $db_type");

  #
  # "many to many" add_now
  #

  # SETUP
  $o = MyInformixObject->new(id   => 50,
                             name => 'Blat',
                             flag => 1);

  $o->delete;

  @colors =
  (
    MyInformixColor->new(id => 1), # red
    MyInformixColor->new(id => 3), # blue  
  );

  #MyInformixColor->new(id => 4), # pink

  $o->colors_on_save(\@colors);
  $o->save;

  $o = MyInformixObject->new(id   => 50,
                             name => 'Blat',
                             flag => 1);
  # Add, no args
  @colors = ();
  ok($o->add_colors(@colors) == 0, "add many to many now 1 - $db_type");

  # Add before load/save
  @colors = 
  (
    MyInformixColor->new(id => 7, name => 'puce'),
    MyInformixColor->new(id => 2), # green
  );

  eval { $o->add_colors(@colors) };

  ok($@, "add many to many now 2 - $db_type");

  # Add
  $o->load;

  $o->add_colors(@colors);

  @colors = $o->colors;
  ok(@colors == 4, "add many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "add many to many now 4 - $db_type");
  ok($colors[1]->id == 2, "add many to many now 5 - $db_type");
  ok($colors[2]->id == 7, "add many to many now 6 - $db_type");
  ok($colors[3]->id == 1, "add many to many now 7 - $db_type");

  ok(MyInformixColor->new(id => 3)->load(speculative => 1), "add many to many now 8 - $db_type");
  ok(MyInformixColor->new(id => 2)->load(speculative => 1), "add many to many now 9 - $db_type");
  ok(MyInformixColor->new(id => 7)->load(speculative => 1), "add many to many now 10 - $db_type");
  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "add many to many now 11 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 50, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 12 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 50, color_id => 2)->load(speculative => 1),
     "set 2 many to many on save 13 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 50, color_id => 7)->load(speculative => 1),
     "set 2 many to many on save 14 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 50, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 15 - $db_type");

  #
  # "many to many" add_on_save
  #

  # SETUP
  $o = MyInformixObject->new(id   => 60,
                             name => 'Cretch',
                             flag => 1);

  $o->delete;

  # Set on save, add on save, save
  @colors = 
  (
    MyInformixColor->new(id => 1), # red
    MyInformixColor->new(id => 2), # green
  );

  # Set on save
  $o->colors_on_save(@colors);

  @colors = 
  (
    MyInformixColor->new(id => 7), # puce
    MyInformixColor->new(id => 8, name => 'tan'),
  );

  # Add on save
  ok($o->add_colors_on_save(@colors), "add many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 2 - $db_type");

  ok($colors[0]->id == 1, "add many to many on save 3 - $db_type");
  ok($colors[1]->id == 2, "add many to many on save 4 - $db_type");
  ok($colors[2]->id == 7, "add many to many on save 5 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 6 - $db_type");

  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MyInformixColor->new(id => 2)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MyInformixColor->new(id => 7)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(!MyInformixColor->new(id => 8)->load(speculative => 1), "add many to many on save 10 - $db_type");

  ok(!MyInformixColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "set many to many on save 11 - $db_type");
  ok(!MyInformixColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "set many to many on save 12 - $db_type");
  ok(!MyInformixColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "set many to many on save 13 - $db_type");
  ok(!MyInformixColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "set many to many on save 14 - $db_type");

  $o->save;

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 15 - $db_type");

  ok($colors[0]->id == 2, "add many to many on save 16 - $db_type");
  ok($colors[1]->id == 7, "add many to many on save 17 - $db_type");
  ok($colors[2]->id == 1, "add many to many on save 18 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 19 - $db_type");

  ok(MyInformixColor->new(id => 2)->load(speculative => 1), "add many to many on save 20 - $db_type");
  ok(MyInformixColor->new(id => 7)->load(speculative => 1), "add many to many on save 21 - $db_type");
  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "add many to many on save 22 - $db_type");
  ok(MyInformixColor->new(id => 8)->load(speculative => 1), "add many to many on save 21 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add many to many on save 22 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add many to many on save 23 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add many to many on save 24 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add many to many on save 25 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 4, "add many to many on save 26 - $db_type");

  # RESET
  $o = MyInformixObject->new(id   => 60,
                             name => 'Cretch',
                             flag => 1);

  $o->load(with => 'colors');

  # Add on save, save
  @colors = 
  (
    MyInformixColor->new(id => 9, name => 'aqua'),
  );

  # Add on save
  ok($o->add_colors_on_save(@colors), "add 2 many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 16 - $db_type");

  ok($colors[0]->id == 2, "add 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 7, "add 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 1, "add 2 many to many on save 4 - $db_type");
  ok($colors[3]->id == 8, "add 2 many to many on save 5 - $db_type");
  ok($colors[4]->id == 9, "add 2 many to many on save 6 - $db_type");

  ok(MyInformixColor->new(id => 2)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MyInformixColor->new(id => 7)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(MyInformixColor->new(id => 8)->load(speculative => 1), "add many to many on save 10 - $db_type");
  ok(!MyInformixColor->new(id => 9)->load(speculative => 1), "add many to many on save 11 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 12 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 13 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 14 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 15 - $db_type");
  ok(!MyInformixColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 16 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  is($count, 4, "add 2 many to many on save 17 - $db_type");

  # Save
  $o->save;

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 18 - $db_type");

  ok($colors[0]->id == 9, "add 2 many to many on save 19 - $db_type");
  ok($colors[1]->id == 2, "add 2 many to many on save 20 - $db_type");
  ok($colors[2]->id == 7, "add 2 many to many on save 21 - $db_type");
  ok($colors[3]->id == 1, "add 2 many to many on save 22 - $db_type");
  ok($colors[4]->id == 8, "add 2 many to many on save 23 - $db_type");

  ok(MyInformixColor->new(id => 9)->load(speculative => 1), "add many to many on save 24 - $db_type");
  ok(MyInformixColor->new(id => 2)->load(speculative => 1), "add many to many on save 25 - $db_type");
  ok(MyInformixColor->new(id => 7)->load(speculative => 1), "add many to many on save 26 - $db_type");
  ok(MyInformixColor->new(id => 1)->load(speculative => 1), "add many to many on save 27 - $db_type");
  ok(MyInformixColor->new(id => 8)->load(speculative => 1), "add many to many on save 28 - $db_type");

  ok(MyInformixColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 29 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 20 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 31 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 32 - $db_type");
  ok(MyInformixColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 33 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 5, "add 2 many to many on save 34 - $db_type");

  # End "many to many" tests
}

#
# SQLite
#

SKIP: foreach my $db_type ('sqlite')
{
  skip("SQLite tests", 465)  unless($HAVE_SQLITE);

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

  my $o = MySQLiteObject->new(name => 'John', id => 1);

  ok(ref $o && $o->isa('MySQLiteObject'), "new() 1 - $db_type");

  $o->flag2('true');
  $o->date_created('now');
  $o->last_modified($o->date_created);
  $o->save_col(7);

  ok($o->save, "save() 1 - $db_type");
  ok($o->load, "load() 1 - $db_type");

  my $o_x = MySQLiteObject->new(id => 99, name => 'John X', flag => 0);
  $o_x->save;

  my $o2 = MySQLiteObject->new(id => $o->id);

  ok(ref $o2 && $o2->isa('MySQLiteObject'), "new() 2 - $db_type");

  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");

  ok($o2->load, "load() 2 - $db_type");
  ok(!$o2->not_found, "not_found() 1 - $db_type");

  is($o2->name, $o->name, "load() verify 1 - $db_type");
  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
  is($o2->save_col, 7, "load() verify 7 (aliased column) - $db_type");
  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");

  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");

  $o2->name('John 2');
  $o2->start('5/24/2001');

  sleep(1); # keep the last modified dates from being the same

  $o2->last_modified('now');
  ok($o2->save, "save() 2 - $db_type");
  ok($o2->load, "load() 3 - $db_type");

  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");

  my $o3 = MySQLiteObject->new();

  my $db = $o3->db or die $o3->error;

  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");

  is($db->dbh, $o3->dbh, "dbh() - $db_type");

  my $o4 = MySQLiteObject->new(id => 999);
  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
  ok($o4->not_found, "not_found() 2 - $db_type");

  ok($o->load, "load() 4 - $db_type");

  my $o5 = MySQLiteObject->new(id => $o->id);

  ok($o5->load, "load() 5 - $db_type");

  $o5->nums([ 4, 5, 6 ]);
  ok($o5->save, "save() 4 - $db_type");
  ok($o->load, "load() 6 - $db_type");

  is($o5->nums->[0], 4, "load() verify 10 (array value) - $db_type");
  is($o5->nums->[1], 5, "load() verify 11 (array value) - $db_type");
  is($o5->nums->[2], 6, "load() verify 12 (array value) - $db_type");

  my @a = $o5->nums;

  is($a[0], 4, "load() verify 13 (array value) - $db_type");
  is($a[1], 5, "load() verify 14 (array value) - $db_type");
  is($a[2], 6, "load() verify 15 (array value) - $db_type");
  is(@a, 3, "load() verify 16 (array value) - $db_type");

  my $oo1 = MySQLiteOtherObject->new(k1 => 1, k2 => 2, k3 => 3, name => 'one');
  ok($oo1->save, "other object save() 1 - $db_type");

  my $oo2 = MySQLiteOtherObject->new(k1 => 11, k2 => 12, k3 => 13, name => 'two');
  ok($oo2->save, "other object save() 2 - $db_type");

  is($o->other_obj, undef, "other_obj() 1 - $db_type");

  $o->fkone(99);
  $o->fk2(99);
  $o->fk3(99);

  eval { $o->other_obj };
  ok($@, "fatal referential_integrity - $db_type");
  ok(!defined $o->other_obj_osoft, "ok referential_integrity 1 - $db_type");
  ok(!defined $o->other_obj_msoft, "ok referential_integrity 2 - $db_type");

  $o->fkone(1);
  $o->fk2(2);
  $o->fk3(3);

  my $obj = $o->other_obj or warn "# ", $o->error, "\n";

  is(ref $obj, 'MySQLiteOtherObject', "other_obj() 2 - $db_type");
  is($obj->name, 'one', "other_obj() 3 - $db_type");

  $o->other_obj(undef);
  $o->fkone(11);
  $o->fk2(12);
  $o->fk3(13);

  ok(!$o->has_loaded_related('other_obj'), "has_loaded_related() 1 - $db_type");

  $obj = $o->other_obj or warn "# ", $o->error, "\n";

  ok($o->has_loaded_related('other_obj'), "has_loaded_related() 2 - $db_type");

  is(ref $obj, 'MySQLiteOtherObject', "other_obj() 4 - $db_type");
  is($obj->name, 'two', "other_obj() 5 - $db_type");

  my $oo21 = MySQLiteOtherObject2->new(id => 1, name => 'one', pid => $o->id);
  ok($oo21->save, "other object 2 save() 1 - $db_type");

  my $oo22 = MySQLiteOtherObject2->new(id => 2, name => 'two', pid => $o->id);
  ok($oo22->save, "other object 2 save() 2 - $db_type");

  my $oo23 = MySQLiteOtherObject2->new(id => 3, name => 'three', pid => $o_x->id);
  ok($oo23->save, "other object 2 save() 3 - $db_type");

  # Begin experiment
  #local $Rose::DB::Object::Manager::Debug = 1;
  my $no2s = $o->not_other2_objs;

  is(scalar @$no2s, 2, "not equal one-to-many 1 - $db_type");

  my $nobjs = 
    Rose::DB::Object::Manager->get_objects(
      object_class => 'MySQLiteObject',
      require_objects => [ 'not_other2_objs' ]);

  is(scalar @$nobjs, 2, "not equal one-to-many 2 - $db_type");

  MySQLiteObject->meta->delete_relationship('not_other2_objs');
  # End experiment

  # Begin manager_*_method tests
  my $manager_method_obj = MySQLiteObject->new(id => $o->id)->load;

  is($manager_method_obj->custom_manager_method_other2_objs, 'ima-get-objects', 
    "custom manager method ima-get-objects - $db_type");

  is($manager_method_obj->meta->relationship('custom_manager_method_other_obj_msoft')->manager_delete_method, 
     'other_obj_delete', "custom manager method ima-delete - $db_type");

  is($manager_method_obj->find_custom_manager_method_other2_objs, 'ima-find', 
     "custom manager method ima-find - $db_type");

  is($manager_method_obj->custom_manager_method_other2_objs_iterator, 'ima-iterator', 
     "custom manager method ima-iterator - $db_type");

  is($manager_method_obj->custom_manager_method_other2_objs_count, 'ima-count', 
     "custom manager method ima-count - $db_type");
  # End manager_*_method tests

  # Begin filtered collection tests

  my $x = MySQLiteObject->new(id => $o->id)->load;
  $x->other2_a_objs({ name => 'aoo' }, { name => 'abc' });

  $x->save;

  $x = MySQLiteObject->new(id => $o->id)->load;

  my $one_o = $x->other2_one_obj;
  my $ao = $x->other2_a_objs;
  my $oo = $x->other2_objs;

  is($one_o->id, 1, "filtered one-to-one 1 - $db_type");
  is(scalar @$ao, 2, "filtered one-to-many 1 - $db_type");
  is(join(',', map { $_->name } @$ao), 'abc,aoo', "filtered one-to-many 2 - $db_type");

  is(scalar @$oo, 4, "filtered one-to-many 3 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'abc,aoo,one,two', "filtered one-to-many 4 - $db_type");

  $x->other2_a_objs({ name => 'axx' });
  $x->save;

  $x = MySQLiteObject->new(id => $o->id)->load;

  $ao = $x->other2_a_objs;
  $oo = $x->other2_objs;

  is(scalar @$ao, 1, "filtered one-to-many 5 - $db_type");
  is(join(',', map { $_->name } @$ao), 'axx', "filtered one-to-many 6 - $db_type");

  is(scalar @$oo, 3, "filtered one-to-many 7 - $db_type");
  is(join(',', sort map { $_->name } @$oo), 'axx,one,two', "filtered one-to-many 8 - $db_type");

  $x->other2_a_objs([]);
  $x->save;

  # End filtered collection tests

  ok(!$o->has_loaded_related('other2_objs'), "has_loaded_related() 3 - $db_type");

  my $o2s = $o->other2_objs;

  ok($o->has_loaded_related('other2_objs'), "has_loaded_related() 4 - $db_type");

  ok(ref $o2s eq 'ARRAY' && @$o2s == 2 && 
     $o2s->[0]->name eq 'two' && $o2s->[1]->name eq 'one',
     'other objects 1');

  my @o2s = $o->other2_objs;

  ok(@o2s == 2 && $o2s[0]->name eq 'two' && $o2s[1]->name eq 'one',
     'other objects 2');

  my $color = MySQLiteColor->new(id => 1, name => 'red');
  ok($color->save, "save color 1 - $db_type");

  $color = MySQLiteColor->new(id => 2, name => 'green');
  ok($color->save, "save color 2 - $db_type");

  $color = MySQLiteColor->new(id => 3, name => 'blue');
  ok($color->save, "save color 3 - $db_type");

  $color = MySQLiteColor->new(id => 4, name => 'pink');
  ok($color->save, "save color 4 - $db_type");

  my $map1 = MySQLiteColorMap->new(obj_id => 1, color_id => 1);
  ok($map1->save, "save color map record 1 - $db_type");

  my $map2 = MySQLiteColorMap->new(obj_id => 1, color_id => 3);
  ok($map2->save, "save color map record 2 - $db_type");

  my $map3 = MySQLiteColorMap->new(obj_id => 99, color_id => 4);
  ok($map3->save, "save color map record 3 - $db_type");

  my $colors = $o->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "colors 1 - $db_type");

  $colors = $o->find_colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'red',
     "find colors 1 - $db_type");

  $colors = $o->find_colors([ name => { like => 'r%' } ]);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red',
     "find colors 2 - $db_type");

  $colors = $o->find_colors(query => [ name => { like => 'r%' } ], cache => 1);

  my $colors2 = $o->find_colors(from_cache => 1);

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'red' &&
     ref $colors2 eq 'ARRAY' && @$colors2 == 1 && $colors2->[0]->name eq 'red' &&
     $colors->[0] eq $colors2->[0],
     "find colors from cache - $db_type");

  ok(my $iterator = $o->colors_iterator, "get colors_iterator - $db_type");

  ok($iterator->isa('Rose::DB::Object::Iterator'),  "colors iterator isa Iterator - $db_type");

  while(my $color = $iterator->next)
  {
    ok($color->name, "color has a name (" . $color->name . ") - $db_type");
  }

  is($iterator->total, 2, "iterator total - $db_type");

  my $count = $o->colors_count;

  is($count, 2, "count colors 1 - $db_type");

  $count = $o->colors_count([ name => { like => 'r%' } ]);

  is($count, 1, "count colors 2 - $db_type");

  my @colors = $o->colors;

  ok(@colors == 2 && $colors[0]->name eq 'blue' && $colors[1]->name eq 'red',
     "colors 2 - $db_type");

  $colors = $o_x->colors;

  ok(ref $colors eq 'ARRAY' && @$colors == 1 && $colors->[0]->name eq 'pink',
     "colors 3 - $db_type");

  @colors = $o_x->colors;

  ok(@colors == 1 && $colors[0]->name eq 'pink', "colors 4 - $db_type");

  $o = MySQLiteObject->new(id => 1)->load;
  $o->fkone(1);
  $o->fk2(2);
  $o->fk3(3);
  $o->save;

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

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $o->delete(cascade => 'null');
  };

  ok($@, "delete cascade null 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MySQLiteOtherObject');

  is($count, 2, "delete cascade rollback confirm 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MySQLiteOtherObject2');

  is($count, 3, "delete cascade rollback confirm 2 - $db_type");

  ok($o->delete(cascade => 'delete'), "delete cascade delete 1 - $db_type");

  $o = MySQLiteObject->new(id => 99)->load;
  $o->fkone(11);
  $o->fk2(12);
  $o->fk3(13);
  $o->save;

  eval
  {
    local $o->dbh->{'PrintError'} = 0;
    $o->delete(cascade => 'null');
  };

  ok($@, "delete cascade null 2 - $db_type");

  ok($o->delete(cascade => 'delete'), "delete cascade delete 2 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MySQLiteColorMap');

  is($count, 0, "delete cascade confirm 1 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MySQLiteOtherObject2');

  is($count, 0, "delete cascade confirm 2 - $db_type");

  $count = 
    Rose::DB::Object::Manager->get_objects_count(
      db => $o->db,
      object_class => 'MySQLiteOtherObject');

  is($count, 0, "delete cascade confirm 3 - $db_type");

  eval { $o->meta->alias_column(nonesuch => 'foo') };
  ok($@, "alias_column() nonesuch - $db_type");

  # Start foreign key method tests

  #
  # Foreign key get_set_now
  #

  $o = MySQLiteObject->new(id   => 50,
                       name => 'Alex',
                       flag => 1);

  eval { $o->other_obj('abc') };
  ok($@, "set foreign key object: one arg - $db_type");

  eval { $o->other_obj(k1 => 1, k2 => 2, k3 => 3) };
  ok($@, "set foreign key object: no save - $db_type");

  $o->save;

  eval
  {
    local $o->db->dbh->{'PrintError'} = 0;
    $o->other_obj(k1 => 1, k2 => 2);
  };

  ok($@, "set foreign key object: too few keys - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 1 - $db_type");
  ok($o->fkone == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 1 - $db_type");

  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "set foreign key object 2 - $db_type");
  ok($o->fkone == 1 && $o->fk2 == 2 && $o->fk3 == 3, "set foreign key object check keys 2 - $db_type");

  #
  # Foreign key delete_now
  #

  ok($o->delete_other_obj, "delete foreign key object 1 - $db_type");

  ok(!defined $o->fkone && !defined $o->fk2 && !defined $o->fk3, "delete foreign key object check keys 1 - $db_type");

  ok(!defined $o->other_obj && defined $o->error, "delete foreign key object confirm 1 - $db_type");

  ok(!defined $o->delete_other_obj, "delete foreign key object 2 - $db_type");

  #
  # Foreign key get_set_on_save
  #

  # TEST: Set, save
  $o = MySQLiteObject->new(id   => 100,
                       name => 'Bub',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 21, k2 => 22, k3 => 23), "set foreign key object on save 1 - $db_type");

  my $co = MySQLiteObject->new(id => 100);
  ok(!$co->load(speculative => 1), "set foreign key object on save 2 - $db_type");

  my $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 3 - $db_type");

  ok($o->save, "set foreign key object on save 4 - $db_type");

  $o = MySQLiteObject->new(id => 100);

  $o->load;

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj && $other_obj->k1 == 21 && $other_obj->k2 == 22 && $other_obj->k3 == 23,
     "set foreign key object on save 5 - $db_type");

  # TEST: Set, set to undef, save
  $o = MySQLiteObject->new(id   => 200,
                       name => 'Rose',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 6 - $db_type");

  $co = MySQLiteObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 7 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 8 - $db_type");

  $o->other_obj_on_save(undef);

  ok($o->save, "set foreign key object on save 9 - $db_type");

  $o = MySQLiteObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 10 - $db_type");

  $co = MySQLiteOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 11 - $db_type");

  $o->delete(cascade => 1);

  # TEST: Set, delete, save
  $o = MySQLiteObject->new(id   => 200,
                       name => 'Rose',
                       flag => 1);

  ok($o->other_obj_on_save(k1 => 51, k2 => 52, k3 => 53), "set foreign key object on save 12 - $db_type");

  $co = MySQLiteObject->new(id => 200);
  ok(!$co->load(speculative => 1), "set foreign key object on save 13 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok($other_obj && $other_obj->k1 == 51 && $other_obj->k2 == 52 && $other_obj->k3 == 53,
     "set foreign key object on save 14 - $db_type");

  ok($o->delete_other_obj, "set foreign key object on save 15 - $db_type");

  $other_obj = $o->other_obj_on_save;

  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "set foreign key object on save 16 - $db_type");

  ok($o->save, "set foreign key object on save 17 - $db_type");

  $o = MySQLiteObject->new(id => 200);

  $o->load;

  ok(!defined $o->other_obj_on_save, "set foreign key object on save 18 - $db_type");

  $co = MySQLiteOtherObject->new(k1 => 51, k2 => 52, k3 => 53);
  ok(!$co->load(speculative => 1), "set foreign key object on save 19 - $db_type");

  $o->delete(cascade => 1);

  #
  # Foreign key delete_on_save
  #

  $o = MySQLiteObject->new(id   => 500,
                       name => 'Kip',
                       flag => 1);

  $o->other_obj_on_save(k1 => 7, k2 => 8, k3 => 9);
  $o->save;

  $o = MySQLiteObject->new(id => 500);
  $o->load;

  # TEST: Delete, save
  $o->del_other_obj_on_save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 1 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MySQLiteOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok($co->load(speculative => 1), "delete foreign key object on save 2 - $db_type");

  # Do the save
  ok($o->save, "delete foreign key object on save 3 - $db_type");

  # Now it's deleted
  $co = MySQLiteOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete foreign key object on save 4 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MySQLiteObject->new(id   => 700,
                       name => 'Ham',
                       flag => 0);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MySQLiteObject->new(id => 700);
  $o->load;

  # TEST: Delete, set on save, delete, save
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 1 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 2 - $db_type");

  # ...but that the foreign object has not yet been deleted
  $co = MySQLiteOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Set on save
  $o->other_obj_on_save(k1 => 44, k2 => 55, k3 => 66);

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 44 && $other_obj->k2 == 55 && $other_obj->k3 == 66,
     "delete 2 foreign key object on save 4 - $db_type");

  # ...and that the foreign object has not yet been saved
  $co = MySQLiteOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 5 - $db_type");

  # Delete again
  ok($o->del_other_obj_on_save, "delete 2 foreign key object on save 6 - $db_type");

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are undef...
  ok(!defined $other_obj && !defined $o->fkone && !defined $o->fk2 && !defined $o->fk3,
     "delete 2 foreign key object on save 7 - $db_type");

  # Confirm that the foreign objects have not been saved
  $co = MySQLiteOtherObject->new(k1 => 7, k2 => 8, k3 => 9);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 8 - $db_type");
  $co = MySQLiteOtherObject->new(k1 => 44, k2 => 55, k3 => 66);
  ok(!$co->load(speculative => 1), "delete 2 foreign key object on save 9 - $db_type");

  # RESET
  $o->delete;

  $o = MySQLiteObject->new(id   => 800,
                       name => 'Lee',
                       flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MySQLiteObject->new(id => 800);
  $o->load;

  # TEST: Set & save, delete on save, set on save, delete on save, save
  ok($o->other_obj(k1 => 1, k2 => 2, k3 => 3), "delete 3 foreign key object on save 1 - $db_type");

  # Confirm that both foreign objects are in the db
  $co = MySQLiteOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 2 - $db_type");
  $co = MySQLiteOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 3 foreign key object on save 3 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to old value
  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);

  # Delete on save
  $o->del_other_obj_on_save;  

  # Save
  $o->save;

  # Confirm that both foreign objects have been deleted
  $co = MySQLiteOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 4 - $db_type");
  $co = MySQLiteOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok(!$co->load(speculative => 1), "delete 3 foreign key object on save 5 - $db_type");

  # RESET
  $o->delete;

  $o = MySQLiteObject->new(id   => 900,
                       name => 'Kai',
                       flag => 1);

  $o->other_obj_on_save(k1 => 12, k2 => 34, k3 => 56);
  $o->save;

  $o = MySQLiteObject->new(id => 900);
  $o->load;

  # TEST: Delete on save, set on save, delete on save, set to same one, save
  $o->del_other_obj_on_save;

  # Set on save
  ok($o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3), "delete 4 foreign key object on save 1 - $db_type");

  # Delete on save
  $o->del_other_obj_on_save;

  # Set-on-save to previous value
  $o->other_obj_on_save(k1 => 1, k2 => 2, k3 => 3);

  # Save
  $o->save;

  $other_obj = $o->other_obj_on_save;

  # Confirm that fk attrs are set...
  ok($other_obj &&  $other_obj->k1 == 1 && $other_obj->k2 == 2 && $other_obj->k3 == 3,
     "delete 4 foreign key object on save 2 - $db_type");

  # Confirm that the new foreign object is there and the old one is not
  $co = MySQLiteOtherObject->new(k1 => 1, k2 => 2, k3 => 3);
  ok($co->load(speculative => 1), "delete 4 foreign key object on save 3 - $db_type");
  $co = MySQLiteOtherObject->new(k1 => 12, k2 => 34, k3 => 56);
  ok(!$co->load(speculative => 1), "delete 4 foreign key object on save 4 - $db_type");

  # End foreign key method tests

  # Start "one to many" method tests

  #
  # "one to many" get_set_now
  #

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

  # SETUP
  $o = MySQLiteObject->new(id   => 111,
                       name => 'Boo',
                       flag => 1);

  @o2s = 
  (
    1,
    MySQLiteOtherObject2->new(id => 2, name => 'two'),
    { id => 3, name => 'three' },
  );

  # Set before save, save, set
  eval { $o->other2_objs_now(@o2s) };
  ok($@, "set one to many now 1 - $db_type");

  $o->save;

  ok($o->other2_objs_now(@o2s), "set one to many now 2 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 3 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 4 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 5 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 6 - $db_type");

  my @fos = $o->find_other2_objs(query    => [ id => { gt => 1 } ],
                                 sort_by  => 'id desc', 
                                 share_db => 0);

  ok($fos[0]->id == 3 && $fos[0]->pid == 111, "find one to many 1 - $db_type");
  ok($fos[1]->id == 2 && $fos[1]->pid == 111, "find one to many 2 - $db_type");
  ok(!defined $fos[0]->{'db'}, "find one to many 3 - $db_type");
  ok(!defined $fos[1]->{'db'}, "find one to many 4 - $db_type");

  @fos = $o->find_other2_objs([ id => { gt => 1 } ],
                              sort_by  => 'id desc', 
                              share_db => 0);

  ok($fos[0]->id == 3 && $fos[0]->pid == 111, "find one to many array query 1 - $db_type");
  ok($fos[1]->id == 2 && $fos[1]->pid == 111, "find one to many array query 2 - $db_type");
  ok(!defined $fos[0]->{'db'}, "find one to many array query 3 - $db_type");
  ok(!defined $fos[1]->{'db'}, "find one to many array query 4 - $db_type");

  @fos = $o->find_other2_objs([ id => 2 ]);

  ok($fos[0]->id == 2 && $fos[0]->pid == 111, "find one to many array query 5 - $db_type");

  @fos = $o->find_other2_objs({ id => { gt => 1 } },
                              sort_by  => 'id desc', 
                              share_db => 0);

  ok($fos[0]->id == 3 && $fos[0]->pid == 111, "find one to many hash query 1 - $db_type");
  ok($fos[1]->id == 2 && $fos[1]->pid == 111, "find one to many hash query 2 - $db_type");
  ok(!defined $fos[0]->{'db'}, "find one to many hash query 3 - $db_type");
  ok(!defined $fos[1]->{'db'}, "find one to many hash query 4 - $db_type");

  @fos = $o->find_other2_objs({ id => 2 });

  ok($fos[0]->id == 2 && $fos[0]->pid == 111, "find one to many hash query 5 - $db_type");

  @fos = $o->find_other2_objs(query    => [ id => { le => 2 } ],
                              sort_by  => 'id desc', 
                              cache    => 1);

  ok($fos[0]->id == 2 && $fos[0]->pid == 111, "find one to many cache 1 - $db_type");
  ok($fos[1]->id == 1 && $fos[1]->pid == 111, "find one to many cache 2 - $db_type");

  my @fos2 = $o->find_other2_objs(from_cache => 1);

  ok($fos2[0] eq $fos[0], "find one to many from_cache 1 - $db_type");
  ok($fos2[1] eq $fos[1], "find one to many from_cache 2 - $db_type");

  ok(my $o2objects_iterator = $o->other2_objs_iterator, "other2_objs_iterator - $db_type");
  ok($o2objects_iterator->isa('Rose::DB::Object::Iterator'), "isa Iterator - $db_type");

  while(my $o2i = $o2objects_iterator->next)
  {
    ok($o2i->isa('MySQLiteOtherObject2'), "isa MySQLiteOtherObject2 - $db_type");
  }

  is($o2objects_iterator->total, 3, "MySQLiteOtherObject2 iterator total - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 7 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 8 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 9 - $db_type");

  my $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 3, "set one to many now 10 - $db_type");

  # Set to undef
  $o->other2_objs_now(undef);

  @o2s = $o->other2_objs_now;
  ok(@o2s == 3, "set one to many now 11 - $db_type");

  ok($o2s[0]->id == 2 && $o2s[0]->pid == 111, "set one to many now 12 - $db_type");
  ok($o2s[1]->id == 3 && $o2s[1]->pid == 111, "set one to many now 13 - $db_type");
  ok($o2s[2]->id == 1 && $o2s[2]->pid == 111, "set one to many now 14 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 15 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 2)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 16 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 3)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many now 17 - $db_type");

  # RESET
  $o = MySQLiteObject->new(id => 111)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 1, name => 'one'),
    MySQLiteOtherObject2->new(id => 7, name => 'seven'),
  );

  ok($o->other2_objs_now(\@o2s), "set 2 one to many now 1 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 1)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 2 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many now 3 - $db_type");

  @o2s = $o->other2_objs_now;
  ok(@o2s == 2, "set 2 one to many now 4 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 111, "set 2 one to many now 5 - $db_type");
  ok($o2s[1]->id == 1 && $o2s[1]->pid == 111, "set 2 one to many now 6 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 111');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 2, "set 2 one to many now 7 - $db_type");

  #
  # "one to many" get_set_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MySQLiteObject->new(id   => 222,
                       name => 'Hap',
                       flag => 1);

  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 5, name => 'five'),
    MySQLiteOtherObject2->new(id => 6, name => 'six'),
    MySQLiteOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 1 - $db_type");

  ok($o2s[0]->id == 5 && $o2s[0]->pid == 222, "set one to many on save 2 - $db_type");
  ok($o2s[1]->id == 6 && $o2s[1]->pid == 222, "set one to many on save 3 - $db_type");
  ok($o2s[2]->id == 7 && $o2s[2]->pid == 222, "set one to many on save 4 - $db_type");

  ok(!MySQLiteOtherObject2->new(id => 5)->load(speculative => 1), "set one to many on save 5 - $db_type");
  ok(!MySQLiteOtherObject2->new(id => 6)->load(speculative => 1), "set one to many on save 6 - $db_type");
  ok(!MySQLiteOtherObject2->new(id => 7)->load(speculative => 1), "set one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 3, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 222, "set one to many on save 9 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 222, "set one to many on save 10 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 222, "set one to many on save 11 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 5)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 12 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 6)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 13 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set one to many on save 14 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 3, "set one to many on save 15 - $db_type");

  # RESET
  $o = MySQLiteObject->new(id => 222)->load;

  # Set (one existing, one new)
  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 7, name => 'seven'),
    MySQLiteOtherObject2->new(id => 12, name => 'one'),
  );

  ok($o->other2_objs_on_save(\@o2s), "set 2 one to many on save 1 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 2 - $db_type");

  ok(!MySQLiteOtherObject2->new(id => 12)->load(speculative => 1), "set 2 one to many on save 3 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 3, "set 2 one to many on save 4 - $db_type");

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set 2 one to many on save 5 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 6 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 7 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 8 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 9 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 10 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 11 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 12 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_other2 WHERE pid = 222');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 2, "set one to many on save 15 - $db_type");

  # Set to undef
  $o->other2_objs_on_save(undef);

  @o2s = $o->other2_objs_on_save;
  ok(@o2s == 2, "set one to many on save 16 - $db_type");

  ok($o2s[0]->id == 7 && $o2s[0]->pid == 222, "set 2 one to many on save 17 - $db_type");
  ok($o2s[1]->id == 12 && $o2s[1]->pid == 222, "set 2 one to many on save 18 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 7)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 19 - $db_type");

  $o2 = MySQLiteOtherObject2->new(id => 12)->load(speculative => 1);
  ok($o2 && $o2->pid == $o->id, "set 2 one to many on save 20 - $db_type");

  $o->save;

  @o2s = $o->other2_objs_on_save;

  push(@o2s, MySQLiteOtherObject2->new(name => 'added'));
  $o->other2_objs_on_save(\@o2s);

  $o->save;

  my $to = MySQLiteObject->new(id => $o->id)->load;
  @o2s = $o->other2_objs_on_save;

  is_deeply([ 'seven', 'one', 'added' ], [ map { $_->name } @o2s ], "add one to many on save 1 - $db_type");

  #
  # "one to many" add_now
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MySQLiteObject->new(id   => 333,
                       name => 'Zoom',
                       flag => 1);

  $o->save;

  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 5, name => 'five'),
    MySQLiteOtherObject2->new(id => 6, name => 'six'),
    MySQLiteOtherObject2->new(id => 7, name => 'seven'),
  );

  $o->other2_objs_now(@o2s);

  # RESET
  $o = MySQLiteObject->new(id   => 333,
                       name => 'Zoom',
                       flag => 1);

  # Add, no args
  @o2s = ();
  ok($o->add_other2_objs_now(@o2s) == 0, "add one to many now 1 - $db_type");

  # Add before load/save
  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 8, name => 'eight'),
  );

  eval { $o->add_other2_objs_now(@o2s) };

  ok($@, "add one to many now 2 - $db_type");

  # Add
  $o->load;

  my $num = $o->add_other2_objs_now(@o2s);
  is($num, scalar @o2s, "add one to many now count - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 4, "add one to many now 3 - $db_type");

  ok($o2s[0]->id == 6 && $o2s[0]->pid == 333, "add one to many now 4 - $db_type");
  ok($o2s[1]->id == 7 && $o2s[1]->pid == 333, "add one to many now 5 - $db_type");
  ok($o2s[2]->id == 5 && $o2s[2]->pid == 333, "add one to many now 6 - $db_type");
  ok($o2s[3]->id == 8 && $o2s[3]->pid == 333, "add one to many now 7 - $db_type");

  ok(MySQLiteOtherObject2->new(id => 6)->load(speculative => 1), "add one to many now 8 - $db_type");
  ok(MySQLiteOtherObject2->new(id => 7)->load(speculative => 1), "add one to many now 9 - $db_type");
  ok(MySQLiteOtherObject2->new(id => 5)->load(speculative => 1), "add one to many now 10 - $db_type");
  ok(MySQLiteOtherObject2->new(id => 8)->load(speculative => 1), "add one to many now 11 - $db_type");

  #
  # "one to many" add_on_save
  #

  # SETUP
  $o2->db->dbh->do('DELETE FROM rose_db_object_other2');

  $o = MySQLiteObject->new(id   => 444,
                       name => 'Blargh',
                       flag => 1);

  # Set on save, add on save, save
  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 10, name => 'ten'),
  );

  # Set on save
  $o->other2_objs_on_save(@o2s);

  @o2s = $o->other2_objs;
  ok(@o2s == 1, "add one to many on save 1 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 2 - $db_type");
  ok(!MySQLiteOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 3 - $db_type");

  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 9, name => 'nine'),
  );

  # Add on save
  $num = $o->add_other2_objs(@o2s);
  is($num, scalar @o2s, "add one to many on save 4 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 5 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 6 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[0]->pid == 444, "add one to many on save 7 - $db_type");

  ok(!MySQLiteOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 8 - $db_type");
  ok(!MySQLiteOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 9 - $db_type");

  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 10 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 11 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 12 - $db_type");

  ok(MySQLiteOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 13 - $db_type");
  ok(MySQLiteOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 14 - $db_type");

  # RESET
  $o = MySQLiteObject->new(id   => 444,
                       name => 'Blargh',
                       flag => 1);

  $o->load;

  # Add on save, save
  @o2s = 
  (
    MySQLiteOtherObject2->new(id => 11, name => 'eleven'),
  );

  # Add on save
  ok($o->add_other2_objs(\@o2s), "add one to many on save 15 - $db_type");

  @o2s = $o->other2_objs;
  ok(@o2s == 2, "add one to many on save 16 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 17 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 18 - $db_type");

  ok(MySQLiteOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 19 - $db_type");
  ok(MySQLiteOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 20 - $db_type");
  ok(!MySQLiteOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 21 - $db_type");

  # Save
  $o->save;

  @o2s = $o->other2_objs;
  ok(@o2s == 3, "add one to many on save 22 - $db_type");

  ok($o2s[0]->id == 10 && $o2s[0]->pid == 444, "add one to many on save 23 - $db_type");
  ok($o2s[1]->id == 9 && $o2s[1]->pid == 444, "add one to many on save 24 - $db_type");
  ok($o2s[2]->id == 11 && $o2s[2]->pid == 444, "add one to many on save 25 - $db_type");

  ok(MySQLiteOtherObject2->new(id => 10)->load(speculative => 1), "add one to many on save 26 - $db_type");
  ok(MySQLiteOtherObject2->new(id => 9)->load(speculative => 1), "add one to many on save 27 - $db_type");
  ok(MySQLiteOtherObject2->new(id => 11)->load(speculative => 1), "add one to many on save 28 - $db_type");

  # End "one to many" method tests

  # Start "load with ..." tests

  ok($o = MySQLiteObject->new(id => 444)->load(with => [ qw(other_obj other2_objs colors) ]),
     "load with 1 - $db_type");

  $o->{'other2_objs'} = [ sort { $a->{'name'} cmp $b->{'name'} } @{$o->{'other2_objs'}} ];

  ok($o->{'other2_objs'} && $o->{'other2_objs'}[1]->name eq 'nine',
     "load with 2 - $db_type");

  $o = MySQLiteObject->new(id => 999);

  ok(!$o->load(with => [ qw(other_obj other2_objs colors) ], speculative => 1),
     "load with 3 - $db_type");

  $o = MySQLiteObject->new(id => 222);

  ok($o->load(with => 'colors'), "load with 4 - $db_type");

  # End "load with ..." tests

  # Start "many to many" tests

  #
  # "many to many" get_set_now
  #

  # SETUP

  $o = MySQLiteObject->new(id   => 30,
                             name => 'Color',
                             flag => 1);

  # Set
  @colors =
  (
    1, # red
    MySQLiteColor->new(id => 3), # blue
    { id => 5, name => 'orange' },
  );

  #MySQLiteColor->new(id => 2), # green
  #MySQLiteColor->new(id => 4), # pink

  # Set before save, save, set
  eval { $o->colors_now(@colors) };
  ok($@, "set many to many now 1 - $db_type");

  $o->save;

  ok($o->colors_now(@colors), "set many to many now 2 - $db_type");

  @colors = $o->colors_now;
  ok(@colors == 3, "set many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "set many to many now 4 - $db_type");
  ok($colors[1]->id == 5, "set many to many now 5 - $db_type");
  ok($colors[2]->id == 1, "set many to many now 6 - $db_type");

  $color = MySQLiteColor->new(id => 5);
  ok($color->load(speculative => 1), "set many to many now 7 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set many to many now 8 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set many to many now 9 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 3, "set many to many now 11 - $db_type");

  # Set to undef
  $o->colors_now(undef);

  @colors = $o->colors_now;
  ok(@colors == 3, "set 2 many to many now 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many now 2 - $db_type");
  ok($colors[1]->id == 5, "set 2 many to many now 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many now 4 - $db_type");

  $color = MySQLiteColor->new(id => 5);
  ok($color->load(speculative => 1), "set 2 many to many now 5 - $db_type");

  $color = MySQLiteColor->new(id => 3);
  ok($color->load(speculative => 1), "set 2 many to many now 6 - $db_type");

  $color = MySQLiteColor->new(id => 1);
  ok($color->load(speculative => 1), "set 2 many to many now 7 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 30, color_id => 3)->load(speculative => 1),
     "set 2 many to many now 8 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 30, color_id => 5)->load(speculative => 1),
     "set 2 many to many now 9 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 30, color_id => 1)->load(speculative => 1),
     "set 2 many to many now 10 - $db_type");

  $sth = $o2->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 30');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 3, "set 2 many to many now 11 - $db_type");

  #
  # "many to many" get_set_on_save
  #

  # SETUP
  $o = MySQLiteObject->new(id   => 40,
                             name => 'Cool',
                             flag => 1);

  # Set
  @colors =
  (
    MySQLiteColor->new(id => 1), # red
    3, # blue
    { id => 6, name => 'ochre' },
  );

  #MySQLiteColor->new(id => 2), # green
  #MySQLiteColor->new(id => 4), # pink

  $o->colors_on_save(@colors);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 1 - $db_type");

  ok($colors[0]->id == 1, "set many to many on save 2 - $db_type");
  ok($colors[1]->id == 3, "set many to many on save 3 - $db_type");
  ok($colors[2]->id == 6, "set many to many on save 4 - $db_type");

  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "set many to many on save 5 - $db_type");
  ok(MySQLiteColor->new(id => 3)->load(speculative => 1), "set many to many on save 6 - $db_type");
  ok(!MySQLiteColor->new(id => 6)->load(speculative => 1), "set many to many on save 7 - $db_type");

  ok(!MySQLiteColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set many to many on save 8 - $db_type");
  ok(!MySQLiteColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set many to many on save 9 - $db_type");
  ok(!MySQLiteColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set many to many on save 10 - $db_type");

  $o->save;

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set many to many on save 11 - $db_type");

  ok($colors[0]->id == 3, "set many to many on save 12 - $db_type");
  ok($colors[1]->id == 6, "set many to many on save 13 - $db_type");
  ok($colors[2]->id == 1, "set many to many on save 14 - $db_type");

  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "set many to many on save 15 - $db_type");
  ok(MySQLiteColor->new(id => 3)->load(speculative => 1), "set many to many on save 16 - $db_type");
  ok(MySQLiteColor->new(id => 6)->load(speculative => 1), "set many to many on save 17 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 18 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 19 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 20 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 3, "set many to many on save 21 - $db_type");

  # RESET
  $o = MySQLiteObject->new(id => 40)->load;

  # Set to undef
  $o->colors_on_save(undef);

  @colors = $o->colors_on_save;
  ok(@colors == 3, "set 2 many to many on save 1 - $db_type");

  ok($colors[0]->id == 3, "set 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 6, "set 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 1, "set 2 many to many on save 4 - $db_type");

  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "set 2 many to many on save 5 - $db_type");
  ok(MySQLiteColor->new(id => 3)->load(speculative => 1), "set 2 many to many on save 6 - $db_type");
  ok(MySQLiteColor->new(id => 6)->load(speculative => 1), "set 2 many to many on save 7 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 40, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 8 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 40, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 9 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 40, color_id => 6)->load(speculative => 1),
     "set 2 many to many on save 10 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 40');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 3, "set 2 many to many on save 11 - $db_type");

  $o->colors([]);
  $o->save(changes_only => 1);

  $o->colors_on_save({ id => 1, name => 'redx' }, { id => 3 });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'blue' && $colors->[1]->name eq 'redx',
     "colors merge 1 - $db_type");

  $o->colors_on_save({ id => 2 }, { id => 3, name => 'bluex' });
  $o->save(changes_only => 1);

  $o->colors_on_save(undef);
  $colors = $o->colors_on_save;

  ok(ref $colors eq 'ARRAY' && @$colors == 2 && 
     $colors->[0]->name eq 'bluex' && $colors->[1]->name eq 'green',
     "colors merge 2 - $db_type");

  #
  # "many to many" add_now
  #

  # SETUP
  $o = MySQLiteObject->new(id   => 50,
                             name => 'Blat',
                             flag => 1);

  $o->delete;

  @colors =
  (
    MySQLiteColor->new(id => 1), # red
    MySQLiteColor->new(id => 3), # blue  
  );

  #MySQLiteColor->new(id => 4), # pink

  $o->colors_on_save(\@colors);
  $o->save;

  $o = MySQLiteObject->new(id   => 50,
                             name => 'Blat',
                             flag => 1);
  # Add, no args
  @colors = ();
  ok($o->add_colors(@colors) == 0, "add many to many now 1 - $db_type");

  # Add before load/save
  @colors = 
  (
    MySQLiteColor->new(id => 7, name => 'puce'),
    MySQLiteColor->new(id => 2), # green
  );

  eval { $o->add_colors(@colors) };

  ok($@, "add many to many now 2 - $db_type");

  # Add
  $o->load;

  $o->add_colors(@colors);

  @colors = $o->colors;
  ok(@colors == 4, "add many to many now 3 - $db_type");

  ok($colors[0]->id == 3, "add many to many now 4 - $db_type");
  ok($colors[1]->id == 2, "add many to many now 5 - $db_type");
  ok($colors[2]->id == 7, "add many to many now 6 - $db_type");
  ok($colors[3]->id == 1, "add many to many now 7 - $db_type");

  ok(MySQLiteColor->new(id => 3)->load(speculative => 1), "add many to many now 8 - $db_type");
  ok(MySQLiteColor->new(id => 2)->load(speculative => 1), "add many to many now 9 - $db_type");
  ok(MySQLiteColor->new(id => 7)->load(speculative => 1), "add many to many now 10 - $db_type");
  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "add many to many now 11 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 50, color_id => 3)->load(speculative => 1),
     "set 2 many to many on save 12 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 50, color_id => 2)->load(speculative => 1),
     "set 2 many to many on save 13 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 50, color_id => 7)->load(speculative => 1),
     "set 2 many to many on save 14 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 50, color_id => 1)->load(speculative => 1),
     "set 2 many to many on save 15 - $db_type");

  #
  # "many to many" add_on_save
  #

  # SETUP
  $o = MySQLiteObject->new(id   => 60,
                             name => 'Cretch',
                             flag => 1);

  $o->delete;

  # Set on save, add on save, save
  @colors = 
  (
    MySQLiteColor->new(id => 1), # red
    MySQLiteColor->new(id => 2), # green
  );

  # Set on save
  $o->colors_on_save(@colors);

  @colors = 
  (
    MySQLiteColor->new(id => 7), # puce
    MySQLiteColor->new(id => 8, name => 'tan'),
  );

  # Add on save
  ok($o->add_colors_on_save(@colors), "add many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 2 - $db_type");

  ok($colors[0]->id == 1, "add many to many on save 3 - $db_type");
  ok($colors[1]->id == 2, "add many to many on save 4 - $db_type");
  ok($colors[2]->id == 7, "add many to many on save 5 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 6 - $db_type");

  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MySQLiteColor->new(id => 2)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MySQLiteColor->new(id => 7)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(!MySQLiteColor->new(id => 8)->load(speculative => 1), "add many to many on save 10 - $db_type");

  ok(!MySQLiteColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "set many to many on save 11 - $db_type");
  ok(!MySQLiteColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "set many to many on save 12 - $db_type");
  ok(!MySQLiteColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "set many to many on save 13 - $db_type");
  ok(!MySQLiteColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "set many to many on save 14 - $db_type");

  $o->save;

  @colors = $o->colors;
  ok(@colors == 4, "add many to many on save 15 - $db_type");

  ok($colors[0]->id == 2, "add many to many on save 16 - $db_type");
  ok($colors[1]->id == 7, "add many to many on save 17 - $db_type");
  ok($colors[2]->id == 1, "add many to many on save 18 - $db_type");
  ok($colors[3]->id == 8, "add many to many on save 19 - $db_type");

  ok(MySQLiteColor->new(id => 2)->load(speculative => 1), "add many to many on save 20 - $db_type");
  ok(MySQLiteColor->new(id => 7)->load(speculative => 1), "add many to many on save 21 - $db_type");
  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "add many to many on save 22 - $db_type");
  ok(MySQLiteColor->new(id => 8)->load(speculative => 1), "add many to many on save 21 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add many to many on save 22 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add many to many on save 23 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add many to many on save 24 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add many to many on save 25 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 4, "add many to many on save 26 - $db_type");

  # RESET
  $o = MySQLiteObject->new(id   => 60,
                             name => 'Cretch',
                             flag => 1);

  $o->load(with => 'colors');

  # Add on save, save
  @colors = 
  (
    MySQLiteColor->new(id => 9, name => 'aqua'),
  );

  # Add on save
  ok($o->add_colors_on_save(@colors), "add 2 many to many on save 1 - $db_type");

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 16 - $db_type");

  @colors = sort { $a->{'name'} cmp $b->{'name'} } @colors;

  ok($colors[0]->id == 9, "add 2 many to many on save 2 - $db_type");
  ok($colors[1]->id == 2, "add 2 many to many on save 3 - $db_type");
  ok($colors[2]->id == 7, "add 2 many to many on save 4 - $db_type");
  ok($colors[3]->id == 1, "add 2 many to many on save 5 - $db_type");
  ok($colors[4]->id == 8, "add 2 many to many on save 6 - $db_type");

  ok(!MySQLiteColor->new(id => 9)->load(speculative => 1), "add many to many on save 7 - $db_type");
  ok(MySQLiteColor->new(id => 2)->load(speculative => 1), "add many to many on save 8 - $db_type");
  ok(MySQLiteColor->new(id => 7)->load(speculative => 1), "add many to many on save 9 - $db_type");
  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "add many to many on save 10 - $db_type");
  ok(MySQLiteColor->new(id => 8)->load(speculative => 1), "add many to many on save 11 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 12 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 13 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 14 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 15 - $db_type");
  ok(!MySQLiteColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 16 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 4, "add 2 many to many on save 17 - $db_type");

  # Save
  $o->save;

  @colors = $o->colors;
  ok(@colors == 5, "add 2 many to many on save 18 - $db_type");

  ok($colors[0]->id == 9, "add 2 many to many on save 19 - $db_type");
  ok($colors[1]->id == 2, "add 2 many to many on save 20 - $db_type");
  ok($colors[2]->id == 7, "add 2 many to many on save 21 - $db_type");
  ok($colors[3]->id == 1, "add 2 many to many on save 22 - $db_type");
  ok($colors[4]->id == 8, "add 2 many to many on save 23 - $db_type");

  ok(MySQLiteColor->new(id => 9)->load(speculative => 1), "add many to many on save 24 - $db_type");
  ok(MySQLiteColor->new(id => 2)->load(speculative => 1), "add many to many on save 25 - $db_type");
  ok(MySQLiteColor->new(id => 7)->load(speculative => 1), "add many to many on save 26 - $db_type");
  ok(MySQLiteColor->new(id => 1)->load(speculative => 1), "add many to many on save 27 - $db_type");
  ok(MySQLiteColor->new(id => 8)->load(speculative => 1), "add many to many on save 28 - $db_type");

  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 9)->load(speculative => 1),
     "add 2 many to many on save 29 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 2)->load(speculative => 1),
     "add 2 many to many on save 20 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 7)->load(speculative => 1),
     "add 2 many to many on save 31 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 1)->load(speculative => 1),
     "add 2 many to many on save 32 - $db_type");
  ok(MySQLiteColorMap->new(obj_id => 60, color_id => 8)->load(speculative => 1),
     "add 2 many to many on save 33 - $db_type");

  $sth = $color->db->dbh->prepare('SELECT COUNT(*) FROM rose_db_object_colors_map WHERE obj_id = 60');
  $sth->execute;
  $count = $sth->fetchrow_array;
  $sth->finish;
  is($count, 5, "add 2 many to many on save 34 - $db_type");

  # End "many to many" tests

  # Begin with_map_records tests

  test_memory_cycle_ok($o, "with_map_records memory cycle 1 - $db_type");
# print find_cycle($o);
# print "######################\n";
#$DB::single = 1;
  @colors = $o->colors2;  
# use Devel::Cycle;
# print find_cycle($o);
# exit;
  is($colors[0]->map_record->color_id, $colors[0]->id, "with_map_records rel 1 - $db_type");
  is($colors[0]->map_record->obj_id, $o->id, "with_map_records rel 2 - $db_type");
#exit;
  @colors = $o->colors3;  

  is($colors[-1]->map_rec->color_id, $colors[-1]->id, "with_map_records rel 3 - $db_type");
  is($colors[-1]->map_rec->obj_id, $o->id, "with_map_records rel 4 - $db_type");
  is($colors[-1]->map_rec->color_id, 1, "with_map_records rel 5 - $db_type");

  my $objs = 
    Rose::DB::Object::Manager->get_objects(
      object_class => 'MySQLiteObject',
      query => [ id => $o->id ],
      require_objects => [ 'colors3' ],
      with_map_records => { colors3 => 'mrec' });

  $objs->[0]->{'colors3'} = [ sort { $b->mrec->color_id <=> $a->mrec->color_id } @{$objs->[0]->{'colors3'}} ];

  is($objs->[0]->colors3->[0]->mrec->color_id, $objs->[0]->colors3->[0]->id, "with_map_records mrec 1 - $db_type");
  is($objs->[0]->colors3->[0]->mrec->obj_id, $o->id, "with_map_records mrec 2 - $db_type");
  is($objs->[0]->colors3->[0]->mrec->color_id, 9, "with_map_records mrec 3 - $db_type");
  is($objs->[0]->colors3->[-1]->mrec->color_id, 1, "with_map_records mrec 4 - $db_type");

  $objs = 
    Rose::DB::Object::Manager->get_objects(
      object_class => 'MySQLiteObject',
      query => [ id => [ $o->id, 333 ] ],
      with_objects => [ 'colors3' ],
      with_map_records => 1,
      sort_by => 'name DESC');

  is($objs->[1]->colors3->[0]->map_rec->color_id, $objs->[1]->colors3->[0]->id, "with_map_records map_rec 1 - $db_type");
  is($objs->[1]->colors3->[0]->map_rec->obj_id, $o->id, "with_map_records map_rec 2 - $db_type");
  is($objs->[1]->colors3->[0]->map_rec->color_id, 9, "with_map_records map_rec 3 - $db_type");
  is($objs->[1]->colors3->[-1]->map_rec->color_id, 1, "with_map_records map_rec 4 - $db_type");
  is($objs->[0]->name, 'Zoom', "with_map_records map_rec 5 - $db_type");

  $objs = 
    Rose::DB::Object::Manager->get_objects(
      object_class => 'MySQLiteObject',
      query => [ id => $o->id ],
      require_objects => [ 'colors2' ],
      with_map_records => 1);

  is($objs->[0]->colors2->[0]->map_record->color_id, $objs->[0]->colors2->[0]->id, "with_map_records map_record 1 - $db_type");
  is($objs->[0]->colors2->[0]->map_record->obj_id, $o->id, "with_map_records map_record 2 - $db_type");

  $objs = 
    Rose::DB::Object::Manager->get_objects(
      object_class => 'MySQLiteObject',
      query => [ id => $o->id ],
      require_objects => [ 'colors2' ],
      with_map_records => 1);

  is($objs->[0]->colors2->[0]->map_record->color_id, $objs->[0]->colors2->[0]->id, "with_map_records map_record 1 - $db_type");
  is($objs->[0]->colors2->[0]->map_record->obj_id, $o->id, "with_map_records map_record 2 - $db_type");

  my $iter = 
    Rose::DB::Object::Manager->get_objects_iterator(
      object_class => 'MySQLiteObject',
      query => [ id => $o->id ],
      require_objects => [ 'colors3' ],
      with_map_records => { colors3 => 'mrec' });

  $obj = $iter->next;

  $obj->{'colors3'} = [ sort { $b->mrec->color_id <=> $a->mrec->color_id } @{$obj->{'colors3'}} ];

  is($obj->colors3->[0]->mrec->color_id, $obj->colors3->[0]->id, "with_map_records mrec 1 - $db_type");
  is($obj->colors3->[0]->mrec->obj_id, $o->id, "with_map_records mrec 2 - $db_type");
  is($obj->colors3->[0]->mrec->color_id, 9, "with_map_records mrec 3 - $db_type");
  is($obj->colors3->[-1]->mrec->color_id, 1, "with_map_records mrec 4 - $db_type");

  $iter = 
    Rose::DB::Object::Manager->get_objects_iterator(
      object_class => 'MySQLiteObject',
      query => [ id => [ $o->id, 333 ] ],
      with_objects => [ 'colors3' ],
      with_map_records => 1,
      sort_by => 'name DESC');

  $obj = $iter->next;
  is($obj->name, 'Zoom', "with_map_records map_rec 5 - $db_type");

  $obj = $iter->next;
  is($obj->colors3->[0]->map_rec->color_id, $obj->colors3->[0]->id, "with_map_records map_rec 1 - $db_type");
  is($obj->colors3->[0]->map_rec->obj_id, $o->id, "with_map_records map_rec 2 - $db_type");
  is($obj->colors3->[0]->map_rec->color_id, 9, "with_map_records map_rec 3 - $db_type");
  is($obj->colors3->[-1]->map_rec->color_id, 1, "with_map_records map_rec 4 - $db_type");

  $iter = 
    Rose::DB::Object::Manager->get_objects_iterator(
      object_class => 'MySQLiteObject',
      query => [ id => $o->id ],
      require_objects => [ 'colors2' ],
      with_map_records => 1);

  $obj = $iter->next;
  is($obj->colors2->[0]->map_record->color_id, $obj->colors2->[0]->id, "with_map_records map_record 1 - $db_type");
  is($obj->colors2->[0]->map_record->obj_id, $o->id, "with_map_records map_record 2 - $db_type");

  $iter = 
    Rose::DB::Object::Manager->get_objects_iterator(
      object_class => 'MySQLiteObject',
      query => [ id => $o->id ],
      require_objects => [ 'colors2' ],
      with_map_records => 1);

  $obj = $iter->next;
  is($obj->colors2->[0]->map_record->color_id, $obj->colors2->[0]->id, "with_map_records map_record 1 - $db_type");
  is($obj->colors2->[0]->map_record->obj_id, $o->id, "with_map_records map_record 2 - $db_type");

  # End with_map_records tests

  # Start create with map records tests

  $o = MySQLiteObject->new(name => 'WMR');

  $o->colors3({ name => 'Gray', map_rec => { id => 999, arb_attr => 'Whee' } });

  $o->save;

  is($o->colors3->[0]->map_rec->arb_attr, 'Whee', "save with map_rec 1 - $db_type");

  $o = MySQLiteColorMap->new(id => 999)->load;

  is($o->arb_attr, 'Whee', "save with map_rec 2 - $db_type");

  # End create with map records tests

  # Start multiple add_on_save tests

  $o = MySQLiteObject->new(name => 'John', id => 10);

  $o->add_other2_objs2({ name => 'xa' }, { name => 'xb' });
  $o->add_other2_objs2({ name => 'xc' });
  $o->save;

  is(join(',', sort map { $_->name } $o->other2_objs2), 'xa,xb,xc', "Multiple add_on_save one-to-many 1 - $db_type");

  $o = MySQLiteObject->new(id => 10)->load;

  $o->add_colors({ name => 'za' }, { name => 'zb' });
  $o->add_colors({ name => 'zc' });
  $o->save;

  is(join(',', sort map { $_->name } $o->colors), 'za,zb,zc', "Multiple add_on_save many-to-many 1 - $db_type");

  $o = MySQLiteObject->new(name => 'John', id => 11);

  $o->other2_objs2;
  $o->add_other2_objs2({ name => 'xa2' }, { name => 'xb2' });
  $o->add_other2_objs2({ name => 'xc2' });

  is(join(',', sort map { $_->name } $o->other2_objs2), 'xa2,xb2,xc2', "Multiple add_on_save one-to-many 2 - $db_type");

  $o->save;

  is(join(',', sort map { $_->name } $o->other2_objs2), 'xa2,xb2,xc2', "Multiple add_on_save one-to-many 3 - $db_type");

  $o = MySQLiteObject->new(id => 11)->load;

  $o->colors;
  $o->add_colors({ name => 'za2' }, { name => 'zb2' });
  $o->add_colors({ name => 'zc2' });

  is(join(',', sort map { $_->name } $o->colors), 'za2,zb2,zc2', "Multiple add_on_save many-to-many 2 - $db_type");

  $o->save;

  is(join(',', sort map { $_->name } $o->colors), 'za2,zb2,zc2', "Multiple add_on_save many-to-many 3 - $db_type");

  # End multiple add_on_save tests

  # Start fk hook-up tests

  $o2 = MySQLiteOtherObject2->new(name => 'B', pid => 11);
  $o2->save;

  $o = MySQLiteObject->new(name => 'John', id => 12);

  $o->add_other2_objs2($o2);
  $o2->name('John2');
  $o->save;

  $o2 = MySQLiteOtherObject2->new(id => $o2->id)->load;

  is($o2->pid, $o->id, "fk hook-up 1 - $db_type");
  is($o2->name, 'John2', "fk hook-up 2 - $db_type");

  # End fk hook-up tests

  test_meta(MySQLiteOtherObject2->meta, 'MySQLite', $db_type);
}

BEGIN
{
  #
  # PostgreSQL
  #

  my $dbh;

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

  if(!$@ && $dbh)
  {
    our $HAVE_PG = 1;

    # Drop existing table and create schema, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rose_db_object_test CASCADE');
      $dbh->do('DROP TABLE rose_db_object_colors_map CASCADE');
      $dbh->do('DROP TABLE rose_db_object_colors');
      $dbh->do('DROP TABLE rose_db_object_other');
      $dbh->do('DROP TABLE rose_db_object_other2');
      $dbh->do('DROP TABLE rose_db_object_chkpass_test');
    }

    our $PG_HAS_CHKPASS = pg_has_chkpass();

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other
(
  k1    INT NOT NULL,
  k2    INT NOT NULL,
  k3    INT NOT NULL,
  name  VARCHAR(32),

  UNIQUE(k1, k2, k3)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_test
(
  id             SERIAL PRIMARY KEY,
  @{[ $PG_HAS_CHKPASS ? 'password CHKPASS,' : '' ]}
  name           VARCHAR(32) NOT NULL,
  flag           BOOLEAN NOT NULL,
  flag2          BOOLEAN,
  status         VARCHAR(32) DEFAULT 'active',
  bits           BIT(5) NOT NULL DEFAULT B'00101',
  start          DATE,
  save           INT,
  nums           INT[],
  fk1            INT,
  fk2            INT,
  fk3            INT,
  last_modified  TIMESTAMP,
  date_created   TIMESTAMP,

  FOREIGN KEY (fk1, fk2, fk3) REFERENCES rose_db_object_other (k1, k2, k3)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other2
(
  id    SERIAL PRIMARY KEY,
  name  VARCHAR(255),
  pid   INT NOT NULL REFERENCES rose_db_object_test (id)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors
(
  id    SERIAL PRIMARY KEY,
  name  VARCHAR(255) NOT NULL
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors_map
(
  id        SERIAL PRIMARY KEY,
  obj_id    INT NOT NULL REFERENCES rose_db_object_test (id),
  color_id  INT NOT NULL REFERENCES rose_db_object_colors (id),

  arb_attr  VARCHAR(64),

  UNIQUE(obj_id, color_id)
)
EOF

    $dbh->disconnect;

    # Create test subclasses

    package MyPgOtherObject;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('pg') }

    MyPgOtherObject->meta->table('rose_db_object_other');

    MyPgOtherObject->meta->columns
    (
      name => { type => 'varchar'},
      k1   => { type => 'int' },
      k2   => { type => 'int' },
      k3   => { type => 'int' },
    );

    MyPgOtherObject->meta->primary_key_columns([ qw(k1 k2 k3) ]);

    MyPgOtherObject->meta->initialize;

    package MyPgObject;

    use Rose::DB::Object::Helpers qw(has_loaded_related forget_related);
    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('pg') }

    MyPgObject->meta->table('rose_db_object_test');

    MyPgObject->meta->columns
    (
      'name',
      id       => { primary_key => 1 },
      ($PG_HAS_CHKPASS ? (password => { type => 'chkpass' }) : ()),
      flag     => { type => 'boolean', default => 1 },
      flag2    => { type => 'boolean' },
      status   => { default => 'active' },
      start    => { type => 'date', default => '12/24/1980' },
      save     => { type => 'scalar' },
      nums     => { type => 'array' },
      bits     => { type => 'bitfield', bits => 5, default => 101 },
      fk1      => { type => 'int' },
      fk2      => { type => 'int' },
      fk3      => { type => 'int' },
      last_modified => { type => 'timestamp' },
      date_created  => { type => 'timestamp' },
    );

    MyPgObject->meta->foreign_keys
    (
      other_obj =>
      {
        class => 'MyPgOtherObject',
        rel_type => 'one to one',
        key_columns =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        methods => 
        {
          get_set_now     => undef,
          get_set_on_save => 'other_obj_on_save',
          delete_now      => undef,
          delete_on_save  => 'del_other_obj_on_save',
        },
      },
    );

    MyPgObject->meta->relationships
    (
      other_objx =>
      {
        type  => 'one to one',
        class => 'MyPgOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        required => 1,
      },

      other2_objs =>
      {
        type  => 'one to many',
        class => 'MyPgOtherObject2',
        column_map => { id => 'pid' },
        manager_args => { sort_by => 'name DESC' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'other2_objs_now',
          get_set_on_save => 'other2_objs_on_save',
          add_now         => 'add_other2_objs_now',
          add_on_save     => undef,
        },
      },

      other2_a_objs =>
      {
        type  => 'one to many',
        class => 'MyPgOtherObject2',
        column_map => { id => 'pid' },
        query_args => [ name => { like => 'a%' } ],
        manager_args => { sort_by => 'name' },
      },

      other_obj_osoft =>
      {
        type => 'one to one',
        class => 'MyPgOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        referential_integrity => 0,
      },

      other_obj_msoft =>
      {
        type => 'many to one',
        class => 'MyPgOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        soft => 1,
      },
    );

    MyPgObject->meta->alias_column(fk1 => 'fkone');

    MyPgObject->meta->add_relationship
    (
      colors =>
      {
        type      => 'many to many',
        map_class => 'MyPgColorMap',
        #map_from  => 'object',
        #map_to    => 'color',
        manager_args => { sort_by => 'rose_db_object_colors.name' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'colors_now',
          get_set_on_save => 'colors_on_save',
          add_now         => undef,
          add_on_save     => 'add_colors_on_save',
          find            => undef,
          count           => undef,
        },
      },
    );

    eval { MyPgObject->meta->initialize };
    Test::More::ok($@, 'meta->initialize() reserved method - pg');

    MyPgObject->meta->alias_column(save => 'save_col');
    MyPgObject->meta->initialize(preserve_existing => 1);

    my $meta = MyPgObject->meta;

    Test::More::is($meta->relationship('other_obj')->foreign_key,
                   $meta->foreign_key('other_obj'),
                   'foreign key sync 1 - pg');

    package MyPgOtherObject2;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('pg') }

    MyPgOtherObject2->meta->table('rose_db_object_other2');

    MyPgOtherObject2->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar'},
      pid  => { type => 'int' },
    );

    MyPgOtherObject2->meta->relationships
    (
      other_obj =>
      {
        type  => 'one to one',
        class => 'MyPgObject',
        column_map => { pid => 'id' },
        required => 1,
      },
    );

    MyPgOtherObject2->meta->foreign_keys
    (
      other_obj =>
      {
        class => 'MyPgObject',
        relationship_type => 'one to one',
        key_columns => { pid => 'id' },
      },
    );

    MyPgOtherObject2->meta->initialize;

    package MyPgColor;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('pg') }

    MyPgColor->meta->table('rose_db_object_colors');

    MyPgColor->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar', not_null => 1 },
    );

    MyPgColor->meta->unique_key('name');

    MyPgColor->meta->initialize;

    package MyPgColorMap;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('pg') }

    MyPgColorMap->meta->table('rose_db_object_colors_map');

    MyPgColorMap->meta->columns
    (
      id       => { type => 'serial', primary_key => 1 },
      obj_id   => { type => 'int', not_null => 1 },
      color_id => { type => 'int', not_null => 1 },
    );

    MyPgColorMap->meta->unique_keys([ 'obj_id', 'color_id' ]);

    MyPgColorMap->meta->foreign_keys
    (
      object =>
      {
        class => 'MyPgObject',
        key_columns => { obj_id => 'id' },
      },

      color =>
      {
        class => 'MyPgColor',
        key_columns => { color_id => 'id' },
      },
    );

    MyPgColorMap->meta->initialize;
  }

  #
  # MySQL
  #

  my $db_version;

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

  if(!$@ && $dbh)
  {
    our $HAVE_MYSQL = 1;

    # Drop existing table and create schema, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rose_db_object_test CASCADE');
      $dbh->do('DROP TABLE rose_db_object_colors_map CASCADE');
      $dbh->do('DROP TABLE rose_db_object_colors');
      $dbh->do('DROP TABLE rose_db_object_other');
      $dbh->do('DROP TABLE rose_db_object_other2');
      $dbh->do('DROP TABLE rose_db_object_chkpass_test');
    }

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other
(
  k1    INT NOT NULL,
  k2    INT NOT NULL,
  k3    INT NOT NULL,
  name  VARCHAR(32),

  KEY(k1, k2, k3)
)
EOF

    # MySQL 5.0.3 or later has a completely stupid "native" BIT type
    my $bit_col = 
      ($db_version >= 5_000_003) ?
        q(bits  BIT(5) NOT NULL DEFAULT B'00101') :
        q(bits  BIT(5) NOT NULL DEFAULT '00101');

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_test
(
  id             INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name           VARCHAR(32) NOT NULL,
  flag           TINYINT(1) NOT NULL,
  flag2          TINYINT(1),
  status         VARCHAR(32) DEFAULT 'active',
  $bit_col,
  start          DATE,
  save           INT,
  fk1            INT,
  fk2            INT,
  fk3            INT,
  last_modified  TIMESTAMP,
  date_created   DATETIME
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other2
(
  id    INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name  VARCHAR(255),
  pid   INT UNSIGNED NOT NULL
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors
(
  id    INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name  VARCHAR(255) NOT NULL
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors_map
(
  id        INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  obj_id    INT NOT NULL REFERENCES rose_db_object_test (id),
  color_id  INT NOT NULL REFERENCES rose_db_object_colors (id),

  arb_attr  VARCHAR(64),

  UNIQUE(obj_id, color_id)
)
EOF

    $dbh->disconnect;

    # Create test subclasses

    package MyMySQLOtherObject;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('mysql') }

    MyMySQLOtherObject->meta->table('rose_db_object_other');

    MyMySQLOtherObject->meta->columns
    (
      name => { type => 'varchar'},
      k1   => { type => 'int' },
      k2   => { type => 'int' },
      k3   => { type => 'int' },
    );

    MyMySQLOtherObject->meta->primary_key_columns([ qw(k1 k2 k3) ]);

    MyMySQLOtherObject->meta->initialize;

    package MyMySQLObject;

    use Rose::DB::Object::Helpers qw(has_loaded_related);
    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('mysql') }

    MyMySQLObject->meta->allow_inline_column_values(1);

    MyMySQLObject->meta->table('rose_db_object_test');

    MyMySQLObject->meta->columns
    (
      'name',
      id       => { primary_key => 1 },
      flag     => { type => 'boolean', default => 1 },
      flag2    => { type => 'boolean' },
      status   => { default => 'active' },
      start    => { type => 'date', default => '12/24/1980' },
      save     => { type => 'scalar' },
      bits     => { type => 'bitfield', bits => 5, default => 101 },
      fk1      => { type => 'int' },
      fk2      => { type => 'int' },
      fk3      => { type => 'int' },
      last_modified => { type => 'timestamp' },
      date_created  => { type => 'datetime' },
    );

    MyMySQLObject->meta->relationships
    (
      other_obj =>
      {
        type  => 'many to one',
        class => 'MyMySQLOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        required => 1,
      }
    );

    MyMySQLObject->meta->add_relationships
    (
      other_obj_otoo =>
      {
        type => 'one to one',
        class => 'MyMySQLOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
      },

      other_obj_osoft =>
      {
        type => 'one to one',
        class => 'MyMySQLOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        referential_integrity => 0,
      },

      other_obj_msoft =>
      {
        type => 'many to one',
        class => 'MyMySQLOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        soft => 1,
        with_column_triggers => 1,
      },

      other2_objs =>
      {
        type  => 'one to many',
        class => 'MyMySQLOtherObject2',
        column_map => { id => 'pid' },
        manager_args => { sort_by => 'rose_db_object_other2.name DESC' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'other2_objs_now',
          get_set_on_save => 'other2_objs_on_save',
          add_now         => 'add_other2_objs_now',
          add_on_save     => undef,
        },
      },

      other2_a_objs =>
      {
        type  => 'one to many',
        class => 'MyMySQLOtherObject2',
        column_map => { id => 'pid' },
        query_args => [ name => { like => 'a%' } ],
        manager_args => { sort_by => 'name' },
      },

      other2_objs2 =>
      {
        type  => 'one to many',
        class => 'MyMySQLOtherObject2',
        column_map => { id => 'pid' },
      },
    );

    MyMySQLObject->meta->foreign_keys
    (
      other_obj =>
      {
        class => 'MyMySQLOtherObject',
        key_columns =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        methods => 
        {
          get_set_now     => undef,
          get_set_on_save => 'other_obj_on_save',
          delete_now      => undef,
          delete_on_save  => 'del_other_obj_on_save',
        },
      },
    );

    MyMySQLObject->meta->add_relationship
    (
      colors =>
      {
        type      => 'many to many',
        map_class => 'MyMySQLColorMap',
        map_from  => 'object',
        map_to    => 'color',
        manager_args => { sort_by => 'rose_db_object_colors.name' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'colors_now',
          get_set_on_save => 'colors_on_save',
          add_now         => undef,
          add_on_save     => 'add_colors_on_save',
          find            => undef,
          count           => undef,
        },
      },
    );

    eval { MyMySQLObject->meta->initialize };
    Test::More::ok($@, 'meta->initialize() reserved method - mysql');

    MyMySQLObject->meta->alias_column(save => 'save_col');
    MyMySQLObject->meta->initialize(preserve_existing => 1);

    my $meta = MyMySQLObject->meta;

    Test::More::is($meta->relationship('other_obj')->foreign_key,
                   $meta->foreign_key('other_obj'),
                   'foreign key sync 1 - mysql');

    package MyMySQLOtherObject2;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('mysql') }

    MyMySQLOtherObject2->meta->table('rose_db_object_other2');

    MyMySQLOtherObject2->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar'},
      pid  => { type => 'int' },
    );

    MyMySQLOtherObject2->meta->relationships
    (
      other_obj =>
      {
        type  => 'many to one',
        class => 'MyMySQLObject',
        column_map => { pid => 'id' },
        required => 1,
        with_column_triggers => 1,
      },
    );

    MyMySQLOtherObject2->meta->foreign_keys
    (
      other_obj =>
      {
        class => 'MyMySQLObject',
        key_columns => { pid => 'id' },
      },
    );

    MyMySQLOtherObject2->meta->initialize;

    package MyMySQLColor;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('mysql') }

    MyMySQLColor->meta->table('rose_db_object_colors');

    MyMySQLColor->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar', not_null => 1 },
    );

    MyMySQLColor->meta->initialize;

    package MyMySQLColorMap;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('mysql') }

    MyMySQLColorMap->meta->table('rose_db_object_colors_map');

    MyMySQLColorMap->meta->columns
    (
      id       => { type => 'serial', primary_key => 1 },
      obj_id   => { type => 'int', not_null => 1 },
      color_id => { type => 'int', not_null => 1 },
    );

    MyMySQLColorMap->meta->unique_keys([ 'obj_id', 'color_id' ]);

    MyMySQLColorMap->meta->foreign_keys
    (
      object =>
      {
        class => 'MyMySQLObject',
        key_columns => { obj_id => 'id' },
      },

      color =>
      {
        class => 'MyMySQLColor',
        key_columns => { color_id => 'id' },
      },
    );

    MyMySQLColorMap->meta->initialize;
  }

  #
  # Informix
  #

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

  if(!$@ && $dbh)
  {
    our $HAVE_INFORMIX = 1;

    # Drop existing table and create schema, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rose_db_object_test CASCADE');
      $dbh->do('DROP TABLE rose_db_object_colors_map CASCADE');
      $dbh->do('DROP TABLE rose_db_object_colors');
      $dbh->do('DROP TABLE rose_db_object_other');
      $dbh->do('DROP TABLE rose_db_object_other2');
      $dbh->do('DROP TABLE rose_db_object_chkpass_test');
    }

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other
(
  k1    INT NOT NULL,
  k2    INT NOT NULL,
  k3    INT NOT NULL,
  name  VARCHAR(32),

  UNIQUE(k1, k2, k3)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_test
(
  id             INT NOT NULL PRIMARY KEY,
  name           VARCHAR(32) NOT NULL,
  flag           BOOLEAN NOT NULL,
  flag2          BOOLEAN,
  status         VARCHAR(32) DEFAULT 'active',
  bits           VARCHAR(5) DEFAULT '00101' NOT NULL,
  start          DATE,
  save           INT,
  nums           VARCHAR(255),
  fk1            INT,
  fk2            INT,
  fk3            INT,
  last_modified  DATETIME YEAR TO FRACTION(5),
  date_created   DATETIME YEAR TO FRACTION(5),

  FOREIGN KEY (fk1, fk2, fk3) REFERENCES rose_db_object_other (k1, k2, k3)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other2
(
  id    INT NOT NULL PRIMARY KEY,
  name  VARCHAR(255),
  pid   INT NOT NULL REFERENCES rose_db_object_test (id)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors
(
  id    SERIAL PRIMARY KEY,
  name  VARCHAR(255) NOT NULL
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors_map
(
  id        SERIAL PRIMARY KEY,
  obj_id    INT NOT NULL REFERENCES rose_db_object_test (id),
  color_id  INT NOT NULL REFERENCES rose_db_object_colors (id),

  arb_attr  VARCHAR(64),

  UNIQUE(obj_id, color_id)
)
EOF

    $dbh->commit;
    $dbh->disconnect;

    # Create test subclasses

    package MyInformixOtherObject;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('informix') }

    MyInformixOtherObject->meta->table('rose_db_object_other');

    MyInformixOtherObject->meta->columns
    (
      name => { type => 'varchar'},
      k1   => { type => 'int' },
      k2   => { type => 'int' },
      k3   => { type => 'int' },
    );

    MyInformixOtherObject->meta->primary_key_columns(qw(k1 k2 k3));

    MyInformixOtherObject->meta->initialize;

    package MyInformixObject;

    use Rose::DB::Object::Helpers qw(has_loaded_related);
    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('informix') }

    MyInformixObject->meta->table('rose_db_object_test');

    MyInformixObject->meta->dbi_prepare_cached(0);

    MyInformixObject->meta->columns
    (
      'name',
      id       => { primary_key => 1 },
      flag     => { type => 'boolean', default => 1 },
      flag2    => { type => 'boolean' },
      status   => { default => 'active' },
      start    => { type => 'date', default => '12/24/1980' },
      save     => { type => 'scalar' },
      nums     => { type => 'array' },
      bits     => { type => 'bitfield', bits => 5, default => 101 },
      fk1      => { type => 'int' },
      fk2      => { type => 'int' },
      fk3      => { type => 'int' },
      last_modified => { type => 'timestamp' },
      date_created  => { type => 'timestamp' },
    );

    MyInformixObject->meta->add_foreign_keys
    (
      other_obj =>
      {
        class => 'MyInformixOtherObject',
        rel_type => 'one to one',
        key_columns =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        methods => 
        {
          get_set_now     => undef,
          get_set_on_save => 'other_obj_on_save',
          delete_now      => undef,
          delete_on_save  => 'del_other_obj_on_save',
        },
      },
    );

    MyInformixObject->meta->add_relationships
    (
      other_obj_osoft =>
      {
        type => 'one to one',
        class => 'MyInformixOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        referential_integrity => 0,
      },

      other_obj_msoft =>
      {
        type => 'many to one',
        class => 'MyInformixOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        soft => 1,
        with_column_triggers => 1,
      },

      other2_objs =>
      {
        type  => 'one to many',
        class => 'MyInformixOtherObject2',
        column_map => { id => 'pid' },
        manager_args => { sort_by => 'rose_db_object_other2.name DESC' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'other2_objs_now',
          get_set_on_save => 'other2_objs_on_save',
          add_now         => 'add_other2_objs_now',
          add_on_save     => undef,
        },
      },

      other2_a_objs =>
      {
        type  => 'one to many',
        class => 'MyInformixOtherObject2',
        column_map => { id => 'pid' },
        query_args => [ name => { like => 'a%' } ],
        manager_args => { sort_by => 'name' },
      },
    );

    MyInformixObject->meta->alias_column(fk1 => 'fkone');

    MyInformixObject->meta->add_relationship
    (
      colors =>
      {
        type      => 'many to many',
        map_class => 'MyInformixColorMap',
        #map_from  => 'object',
        #map_to    => 'color',
        manager_args => { sort_by => 'rose_db_object_colors.name' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'colors_now',
          get_set_on_save => 'colors_on_save',
          add_now         => undef,
          add_on_save     => 'add_colors_on_save',
          find            => undef,
          count           => undef,
        },
      },
    );

    eval { MyInformixObject->meta->initialize };
    Test::More::ok($@, 'meta->initialize() reserved method - informix');

    MyInformixObject->meta->alias_column(save => 'save_col');
    MyInformixObject->meta->initialize(preserve_existing => 1);

    my $meta = MyInformixObject->meta;

    Test::More::is($meta->relationship('other_obj')->foreign_key,
                   $meta->foreign_key('other_obj'),
                   'foreign key sync 1 - Informix');

    package MyInformixOtherObject2;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('informix') }

    MyInformixOtherObject2->meta->table('rose_db_object_other2');

    MyInformixOtherObject2->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar'},
      pid  => { type => 'int' },
    );

    MyInformixOtherObject2->meta->relationships
    (
      other_obj =>
      {
        type  => 'many to one',
        class => 'MyInformixObject',
        column_map => { pid => 'id' },
        required => 1,
        with_column_triggers => 1,
      },
    );

    MyInformixOtherObject2->meta->foreign_keys
    (
      other_obj =>
      {
        class => 'MyInformixObject',
        key_columns => { pid => 'id' },
      },
    );

    MyInformixOtherObject2->meta->initialize;

    package MyInformixColor;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('informix') }

    MyInformixColor->meta->table('rose_db_object_colors');

    MyInformixColor->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar', not_null => 1 },
    );

    MyInformixColor->meta->initialize;

    package MyInformixColorMap;

    our @ISA = qw(Rose::DB::Object);

    our $DB;

    sub init_db { $DB ||= Rose::DB->new('informix') }

    MyInformixColorMap->meta->table('rose_db_object_colors_map');

    MyInformixColorMap->meta->columns
    (
      id       => { type => 'serial', primary_key => 1 },
      obj_id   => { type => 'int', not_null => 1 },
      color_id => { type => 'int', not_null => 1 },
    );

    MyInformixColorMap->meta->unique_keys([ 'obj_id', 'color_id' ]);

    MyInformixColorMap->meta->foreign_keys
    (
      object =>
      {
        class => 'MyInformixObject',
        key_columns => { obj_id => 'id' },
      },

      color =>
      {
        class => 'MyInformixColor',
        key_columns => { color_id => 'id' },
      },
    );

    MyInformixColorMap->meta->initialize;
  }

  #
  # SQLite
  #

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

  if(!$@ && $dbh)
  {
    our $HAVE_SQLITE = 1;

    # Drop existing table and create schema, ignoring errors
    {
      local $dbh->{'RaiseError'} = 0;
      local $dbh->{'PrintError'} = 0;
      $dbh->do('DROP TABLE rose_db_object_test');
      $dbh->do('DROP TABLE rose_db_object_colors_map');
      $dbh->do('DROP TABLE rose_db_object_colors');
      $dbh->do('DROP TABLE rose_db_object_other');
      $dbh->do('DROP TABLE rose_db_object_other2');
      $dbh->do('DROP TABLE rose_db_object_chkpass_test');
    }

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other
(
  k1    INT NOT NULL,
  k2    INT NOT NULL,
  k3    INT NOT NULL,
  name  VARCHAR(32),

  UNIQUE(k1, k2, k3)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_test
(
  id             INTEGER PRIMARY KEY AUTOINCREMENT,
  name           VARCHAR(32) NOT NULL,
  flag           BOOLEAN NOT NULL,
  flag2          BOOLEAN,
  status         VARCHAR(32) DEFAULT 'active',
  bits           VARCHAR(5) DEFAULT '00101' NOT NULL,
  start          DATE,
  save           INT,
  nums           VARCHAR(255),
  fk1            INT,
  fk2            INT,
  fk3            INT,
  last_modified  DATETIME,
  date_created   DATETIME,

  FOREIGN KEY (fk1, fk2, fk3) REFERENCES rose_db_object_other (k1, k2, k3)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other2
(
  id    INTEGER PRIMARY KEY AUTOINCREMENT,
  name  VARCHAR(255),
  pid   INT NOT NULL REFERENCES rose_db_object_test (id)
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors
(
  id    INTEGER PRIMARY KEY AUTOINCREMENT,
  name  VARCHAR(255) NOT NULL
)
EOF

    $dbh->do(<<"EOF");
CREATE TABLE rose_db_object_colors_map
(
  id        INTEGER PRIMARY KEY AUTOINCREMENT,
  obj_id    INT NOT NULL REFERENCES rose_db_object_test (id),
  color_id  INT NOT NULL REFERENCES rose_db_object_colors (id),

  arb_attr  VARCHAR(64),

  UNIQUE(obj_id, color_id)
)
EOF

    $dbh->disconnect;

    # Create test subclasses

    package MySQLiteOtherObject;

    our @ISA = qw(Rose::DB::Object);

    sub init_db { Rose::DB->new('sqlite') }

    MySQLiteOtherObject->meta->table('rose_db_object_other');

    MySQLiteOtherObject->meta->columns
    (
      name => { type => 'varchar'},
      k1   => { type => 'int' },
      k2   => { type => 'int' },
      k3   => { type => 'int' },
    );

    MySQLiteOtherObject->meta->primary_key_columns(qw(k1 k2 k3));

    MySQLiteOtherObject->meta->initialize;

    package MySQLiteObject;

    use Rose::DB::Object::Helpers qw(has_loaded_related);
    our @ISA = qw(Rose::DB::Object);

    sub init_db { Rose::DB->new('sqlite') }

    MySQLiteObject->meta->table('rose_db_object_test');

    MySQLiteObject->meta->columns
    (
      'name',
      id       => { primary_key => 1 },
      flag     => { type => 'boolean', default => 1 },
      flag2    => { type => 'boolean' },
      status   => { default => 'active' },
      start    => { type => 'date', default => '12/24/1980' },
      save     => { type => 'scalar' },
      nums     => { type => 'array' },
      bits     => { type => 'bitfield', bits => 5, default => 101 },
      fk1      => { type => 'int' },
      fk2      => { type => 'int' },
      fk3      => { type => 'int' },
      last_modified => { type => 'timestamp' },
      date_created  => { type => 'timestamp' },
    );

    MySQLiteObject->meta->add_foreign_keys
    (
      other_obj =>
      {
        class => 'MySQLiteOtherObject',
        rel_type => 'one to one',
        key_columns =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        methods => 
        {
          get_set_now     => undef,
          get_set_on_save => 'other_obj_on_save',
          delete_now      => undef,
          delete_on_save  => 'del_other_obj_on_save',
        },
      },
    );

    MySQLiteObject->meta->add_relationships
    (
      other_obj_osoft =>
      {
        type => 'one to one',
        class => 'MySQLiteOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        soft => 1,
      },

      other_obj_msoft =>
      {
        type => 'many to one',
        class => 'MySQLiteOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        referential_integrity => 0,
        with_column_triggers => 1,
      },

      other2_objs =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
        manager_args => { sort_by => 'rose_db_object_other2.name DESC' },
        methods =>
        {
          find            => undef,
          iterator        => undef,
          get_set         => undef,
          get_set_now     => 'other2_objs_now',
          get_set_on_save => 'other2_objs_on_save',
          add_now         => 'add_other2_objs_now',
          add_on_save     => undef,
        },
      },

      other2_a_objs =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
        query_args => [ name => { like => 'a%' } ],
        manager_args => { sort_by => 'name' },
      },

      other2_one_obj =>
      {
        type  => 'one to one',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
        query_args => [ name => 'one' ],
        with_column_triggers => 1,
      },

      other2_objs2 =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
      },

      # Hrm.  Experimental...
      not_other2_objs =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        #column_map => { id => 'pid' },
        query_args => [ id => { ne_sql => 'pid' } ],
      },
    );    

    MySQLiteObject->meta->alias_column(fk1 => 'fkone');

    MySQLiteObject->meta->add_relationship
    (
      colors =>
      {
        type      => 'many to many',
        map_class => 'MySQLiteColorMap',
        #map_from  => 'object',
        #map_to    => 'color',
        manager_args => { sort_by => 'name' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'colors_now',
          get_set_on_save => 'colors_on_save',
          add_now         => undef,
          add_on_save     => 'add_colors_on_save',
          find            => undef,
          count           => undef,
          iterator        => undef,
        },
      },

      colors2 =>
      {
        type      => 'many to many',
        map_class => 'MySQLiteColorMap',
        manager_args => { sort_by => 'name', with_map_records => 1 },
      },

      colors3 =>
      {
        type      => 'many to many',
        map_class => 'MySQLiteColorMap',
        manager_args => { sort_by => 'rose_db_object_colors_map.color_id DESC', with_map_records => 'map_rec' },
      },
    );

    # Test to confirm a 0.780 fix for a bug in delete_relationships()
    MySQLiteObject->meta->delete_relationships;

    MySQLiteObject->meta->add_relationships
    (
      other_obj_osoft =>
      {
        type => 'one to one',
        class => 'MySQLiteOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        soft => 1,
      },

      other_obj_msoft =>
      {
        type => 'many to one',
        class => 'MySQLiteOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        referential_integrity => 0,
        with_column_triggers => 1,
      },

      other2_objs =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
        manager_args => { sort_by => 'rose_db_object_other2.name DESC' },
        methods =>
        {
          find            => undef,
          iterator        => undef,
          get_set         => undef,
          get_set_now     => 'other2_objs_now',
          get_set_on_save => 'other2_objs_on_save',
          add_now         => 'add_other2_objs_now',
          add_on_save     => undef,
        },
      },

      other2_a_objs =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
        query_args => [ name => { like => 'a%' } ],
        manager_args => { sort_by => 'name' },
      },

      other2_one_obj =>
      {
        type  => 'one to one',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
        query_args => [ name => 'one' ],
        with_column_triggers => 1,
      },

      other2_objs2 =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        column_map => { id => 'pid' },
      },

      # Hrm.  Experimental...
      not_other2_objs =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        #column_map => { id => 'pid' },
        query_args => [ id => { ne_sql => 'pid' } ],
      },

      # manager_*_methods
      custom_manager_method_other2_objs =>
      {
        type  => 'one to many',
        class => 'MySQLiteOtherObject2',
        manager_class => 'MySQLiteOtherObject2::Manager',
        manager_method => 'other2_objs',
        manager_count_method => 'other2_objs_count',
        manager_iterator_method => 'other2_objs_iterator',
        manager_find_method => 'other2_objs_find',
        column_map => { id => 'pid' },
        methods =>
        {
          count           => undef,
          find            => undef,
          iterator        => undef,
          get_set         => undef,
          get_set_now     => undef,
          get_set_on_save => undef,
        },
      },

      custom_manager_method_other_obj_msoft =>
      {
        type => 'many to one',
        class => 'MySQLiteOtherObject',
        column_map =>
        {
          fk1 => 'k1',
          fk2 => 'k2',
          fk3 => 'k3',
        },
        referential_integrity => 0,
        with_column_triggers => 1,
        manager_class => 'MySQLiteOtherObject::Manager',
        manager_delete_method => 'other_obj_delete',    # TODO this not yet exercised
      },

    );    

    MySQLiteObject->meta->alias_column(fk1 => 'fkone');

    MySQLiteObject->meta->add_relationship
    (
      colors =>
      {
        type      => 'many to many',
        map_class => 'MySQLiteColorMap',
        #map_from  => 'object',
        #map_to    => 'color',
        manager_args => { sort_by => 'name' },
        methods =>
        {
          get_set         => undef,
          get_set_now     => 'colors_now',
          get_set_on_save => 'colors_on_save',
          add_now         => undef,
          add_on_save     => 'add_colors_on_save',
          find            => undef,
          count           => undef,
          iterator        => undef,
        },
      },

      colors2 =>
      {
        type      => 'many to many',
        map_class => 'MySQLiteColorMap',
        manager_args => { sort_by => 'name', with_map_records => 1 },
      },

      colors3 =>
      {
        type      => 'many to many',
        map_class => 'MySQLiteColorMap',
        manager_args => { sort_by => 'rose_db_object_colors_map.color_id DESC', with_map_records => 'map_rec' },
      },
    );

    eval { MySQLiteObject->meta->initialize };
    Test::More::ok($@, 'meta->initialize() reserved method - sqlite');

    MySQLiteObject->meta->alias_column(save => 'save_col');
    MySQLiteObject->meta->initialize(preserve_existing => 1);

    my $meta = MySQLiteObject->meta;

    Test::More::is($meta->relationship('other_obj')->foreign_key,
                   $meta->foreign_key('other_obj'),
                   'foreign key sync 1 - SQLite');

    package MySQLiteOtherObject2;

    our @ISA = qw(Rose::DB::Object);

    sub init_db { Rose::DB->new('sqlite') }

    MySQLiteOtherObject2->meta->table('rose_db_object_other2');

    MySQLiteOtherObject2->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar'},
      pid  => { type => 'int' },
    );

    MySQLiteOtherObject2->meta->relationships
    (
      other_obj =>
      {
        type  => 'many to one',
        class => 'MySQLiteObject',
        column_map => { pid => 'id' },
        required => 1,
        with_column_triggers => 1,
      },
    );

    MySQLiteOtherObject2->meta->foreign_keys
    (
      other_obj =>
      {
        class => 'MySQLiteObject',
        key_columns => { pid => 'id' },
      },
    );

    MySQLiteOtherObject2->meta->initialize;

    # Manager used only for custom manager_*_methods
    package MySQLiteOtherObject2::Manager;

    sub other2_objs          { 'ima-get-objects' }
    sub other2_objs_count    { 'ima-count' }
    sub other2_objs_iterator { 'ima-iterator' }
    sub other2_objs_find     { 'ima-find' }

    package MySQLiteOtherObject::Manager;

    sub other_obj_delete { 'ima-delete' } # TODO this not yet exercised

    package MySQLiteColor;

    our @ISA = qw(Rose::DB::Object);

    sub init_db { Rose::DB->new('sqlite') }

    MySQLiteColor->meta->table('rose_db_object_colors');

    MySQLiteColor->meta->columns
    (
      id   => { type => 'serial', primary_key => 1 },
      name => { type => 'varchar', not_null => 1 },
    );

    MySQLiteColor->meta->initialize;

    package MySQLiteColorMap;

    our @ISA = qw(Rose::DB::Object);

    sub init_db { Rose::DB->new('sqlite') }

    MySQLiteColorMap->meta->table('rose_db_object_colors_map');

    MySQLiteColorMap->meta->columns
    (
      id       => { type => 'serial', primary_key => 1 },
      obj_id   => { type => 'int', not_null => 1 },
      color_id => { type => 'int', not_null => 1 },
      arb_attr => { type => 'varchar', length => 64 },
    );

    MySQLiteColorMap->meta->unique_keys([ 'obj_id', 'color_id' ]);

    MySQLiteColorMap->meta->foreign_keys
    (
      object =>
      {
        class => 'MySQLiteObject',
        key_columns => { obj_id => 'id' },
      },

      color =>
      {
        class => 'MySQLiteColor',
        key_columns => { color_id => 'id' },
      },
    );

    MySQLiteColorMap->meta->initialize;
  }
}

sub test_meta
{
  my($meta, $prefix, $db_type) = @_;

  $meta->delete_relationships;  

  $meta->delete_foreign_keys;

  $meta->foreign_keys
  (
    other_obj =>
    {
      class => "${prefix}Object",
      key_columns => { pid => 'id' },
    },
  );

  $meta->relationships
  (
    other_objx =>
    {
      type  => 'many to one',
      class => "${prefix}Object",
      column_map => { pid => 'id' },
      required => 1,
      with_column_triggers => 1,
    },
  );

  is(scalar @{$meta->foreign_keys}, 1, "proxy relationships 1 - $db_type");
  is(scalar @{$meta->relationships}, 2, "proxy relationships 2 - $db_type");

  $meta->delete_foreign_keys;

  is(scalar @{$meta->foreign_keys}, 0, "proxy relationships 3 - $db_type");
  is(scalar @{$meta->relationships}, 1, "proxy relationships 4 - $db_type");

  $meta->relationships
  (
    other_objx =>
    {
      type  => 'many to one',
      class => "${prefix}Object",
      column_map => { pid => 'id' },
      required => 1,
      with_column_triggers => 1,
    },
  );

  $meta->foreign_keys
  (
    other_obj =>
    {
      class => "${prefix}Object",
      key_columns => { pid => 'id' },
    },
  );

  is(scalar @{$meta->foreign_keys}, 1, "proxy relationships 5 - $db_type");
  is(scalar @{$meta->relationships}, 2, "proxy relationships 6 - $db_type");
}

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 rose_db_object_test CASCADE');
    $dbh->do('DROP TABLE rose_db_object_colors_map CASCADE');
    $dbh->do('DROP TABLE rose_db_object_colors');
    $dbh->do('DROP TABLE rose_db_object_other');
    $dbh->do('DROP TABLE rose_db_object_other2');

    $dbh->disconnect;
  }

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

    $dbh->do('DROP TABLE rose_db_object_colors_map CASCADE');
    $dbh->do('DROP TABLE rose_db_object_colors');
    $dbh->do('DROP TABLE rose_db_object_test CASCADE');
    $dbh->do('DROP TABLE rose_db_object_other');
    $dbh->do('DROP TABLE rose_db_object_other2');

    $dbh->disconnect;
  }

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

    $dbh->do('DROP TABLE rose_db_object_colors_map CASCADE');
    $dbh->do('DROP TABLE rose_db_object_colors');
    $dbh->do('DROP TABLE rose_db_object_test CASCADE');
    $dbh->do('DROP TABLE rose_db_object_other');
    $dbh->do('DROP TABLE rose_db_object_other2');

    $dbh->disconnect;
  }

  if($HAVE_SQLITE)
  {
    # SQLite
    my $dbh = Rose::DB->new('sqlite_admin')->retain_dbh()
      or die Rose::DB->error;

    $dbh->do('DROP TABLE rose_db_object_colors_map');
    $dbh->do('DROP TABLE rose_db_object_test');
    $dbh->do('DROP TABLE rose_db_object_colors');
    $dbh->do('DROP TABLE rose_db_object_other');
    $dbh->do('DROP TABLE rose_db_object_other2');

    $dbh->disconnect;
  }
}