1#!/usr/bin/perl -w
2
3use strict;
4
5use Test::More tests => 598;
6
7BEGIN
8{
9  require 't/test-lib.pl';
10  use_ok('Rose::DB::Object');
11  use_ok('Rose::DB::Object::Util');
12}
13
14Rose::DB::Object::Util->import(':all');
15
16eval { require Time::HiRes };
17our $Have_HiRes_Time = $@ ? 0 : 1;
18
19our($PG_HAS_CHKPASS, $HAVE_PG, $HAVE_MYSQL, $HAVE_INFORMIX, $HAVE_SQLITE,
20    $HAVE_ORACLE, $INNODB);
21
22#
23# PostgreSQL
24#
25
26SKIP: foreach my $db_type (qw(pg pg_with_schema))
27{
28  skip("PostgreSQL tests", 242)  unless($HAVE_PG);
29
30  Rose::DB->default_type($db_type);
31
32  TEST_HACK:
33  {
34    no warnings;
35    *MyPgObject::init_db = sub { Rose::DB->new($db_type) };
36  }
37
38  my $o = MyPgObject->new(name => 'John',
39                          k1   => 1,
40                          k2   => undef,
41                          k3   => 3);
42
43  ok(ref $o && $o->isa('MyPgObject'), "new() 1 - $db_type");
44
45  $o->flag2('TRUE');
46  $o->date_created('now');
47  $o->date_created_tz('now');
48  $o->timestamp_tz2('now');
49  $o->last_modified($o->date_created);
50  $o->save_col(7);
51
52  if(rand >= 0.5)
53  {
54    ok($o->save, "save() 1 - $db_type");
55  }
56  else
57  {
58    ok($o->insert, "insert() 1 - $db_type");
59  }
60
61  MyPgObject->meta->sql_qualify_column_names_on_load(1);
62
63  my $schema = $db_type eq 'pg_with_schema' ? 'rose_db_object_private.' : '';
64
65  is(MyPgObject->meta->load_all_sql(undef, $o->db),
66     qq(SELECT rose_db_object_test.name, rose_db_object_test.code, rose_db_object_test.id, rose_db_object_test.k1, rose_db_object_test.k2, rose_db_object_test.k3,@{[ $PG_HAS_CHKPASS ? ' rose_db_object_test.passwd,' : '' ]} rose_db_object_test.flag, rose_db_object_test.flag2, rose_db_object_test.status, rose_db_object_test.start, rose_db_object_test.save, rose_db_object_test.nums, rose_db_object_test.bitz, rose_db_object_test.decs, rose_db_object_test.dur, rose_db_object_test.epoch, rose_db_object_test.hiepoch, rose_db_object_test.bint1, rose_db_object_test.bint2, rose_db_object_test.bint3, rose_db_object_test.bint4, rose_db_object_test.tee_time, rose_db_object_test.tee_time0, rose_db_object_test.tee_time5, rose_db_object_test.tee_time9, rose_db_object_test.date_created, rose_db_object_test.date_created_tz, rose_db_object_test.timestamp_tz2, rose_db_object_test.last_modified FROM ${schema}rose_db_object_test WHERE rose_db_object_test.id = ?),
67     "sql_qualify_column_names_on_load() 1 - $db_type");
68
69  is(MyPgObject->meta->load_sql(undef, $o->db),
70     qq(SELECT rose_db_object_test.name, rose_db_object_test.code, rose_db_object_test.id, rose_db_object_test.k1, rose_db_object_test.k3,@{[ $PG_HAS_CHKPASS ? ' rose_db_object_test.passwd,' : '' ]} rose_db_object_test.flag, rose_db_object_test.flag2, rose_db_object_test.status, rose_db_object_test.save, rose_db_object_test.nums, rose_db_object_test.bitz, rose_db_object_test.decs, rose_db_object_test.dur, rose_db_object_test.epoch, rose_db_object_test.hiepoch, rose_db_object_test.bint1, rose_db_object_test.bint2, rose_db_object_test.bint3, rose_db_object_test.bint4, rose_db_object_test.tee_time, rose_db_object_test.tee_time0, rose_db_object_test.tee_time5, rose_db_object_test.tee_time9, rose_db_object_test.date_created, rose_db_object_test.date_created_tz, rose_db_object_test.timestamp_tz2, rose_db_object_test.last_modified FROM ${schema}rose_db_object_test WHERE rose_db_object_test.id = ?),
71     "sql_qualify_column_names_on_load() 2 - $db_type");
72
73  is(MyPgObject->meta->load_all_sql_with_null_key([ qw(k1 k2 k3) ], [ 1, undef, 3 ], $o->db),
74     qq(SELECT rose_db_object_test.name, rose_db_object_test.code, rose_db_object_test.id, rose_db_object_test.k1, rose_db_object_test.k2, rose_db_object_test.k3,@{[ $PG_HAS_CHKPASS ? ' rose_db_object_test.passwd,' : '' ]} rose_db_object_test.flag, rose_db_object_test.flag2, rose_db_object_test.status, rose_db_object_test.start, rose_db_object_test.save, rose_db_object_test.nums, rose_db_object_test.bitz, rose_db_object_test.decs, rose_db_object_test.dur, rose_db_object_test.epoch, rose_db_object_test.hiepoch, rose_db_object_test.bint1, rose_db_object_test.bint2, rose_db_object_test.bint3, rose_db_object_test.bint4, rose_db_object_test.tee_time, rose_db_object_test.tee_time0, rose_db_object_test.tee_time5, rose_db_object_test.tee_time9, rose_db_object_test.date_created, rose_db_object_test.date_created_tz, rose_db_object_test.timestamp_tz2, rose_db_object_test.last_modified FROM ${schema}rose_db_object_test WHERE rose_db_object_test.k1 = ? AND rose_db_object_test.k2 IS NULL AND rose_db_object_test.k3 = ?),
75     "sql_qualify_column_names_on_load() 3 - $db_type");
76
77  is(MyPgObject->meta->load_sql_with_null_key([ qw(k1 k2 k3) ], [ 1, undef, 3 ], $o->db),
78     qq(SELECT rose_db_object_test.name, rose_db_object_test.code, rose_db_object_test.id, rose_db_object_test.k1, rose_db_object_test.k3,@{[ $PG_HAS_CHKPASS ? ' rose_db_object_test.passwd,' : '' ]} rose_db_object_test.flag, rose_db_object_test.flag2, rose_db_object_test.status, rose_db_object_test.save, rose_db_object_test.nums, rose_db_object_test.bitz, rose_db_object_test.decs, rose_db_object_test.dur, rose_db_object_test.epoch, rose_db_object_test.hiepoch, rose_db_object_test.bint1, rose_db_object_test.bint2, rose_db_object_test.bint3, rose_db_object_test.bint4, rose_db_object_test.tee_time, rose_db_object_test.tee_time0, rose_db_object_test.tee_time5, rose_db_object_test.tee_time9, rose_db_object_test.date_created, rose_db_object_test.date_created_tz, rose_db_object_test.timestamp_tz2, rose_db_object_test.last_modified FROM ${schema}rose_db_object_test WHERE rose_db_object_test.k1 = ? AND rose_db_object_test.k2 IS NULL AND rose_db_object_test.k3 = ?),
79     "sql_qualify_column_names_on_load() 4 - $db_type");
80
81  MyPgObject->meta->sql_qualify_column_names_on_load(rand > 0.6 ? 0 : 1); # excitement! :)
82
83  is($o->meta->primary_key->sequence_names->[0], 'rose_db_object_test_id_seq',
84     "pk sequence name - $db_type");
85
86  ok(is_in_db($o), "is_in_db - $db_type");
87
88  is($o->id, 1, "auto-generated primary key - $db_type");
89
90  ok($o->load, "load() 1 - $db_type");
91
92  is($o->date_created->time_zone->name, 'floating', "timestamp without time zone - $db_type");
93  isnt($o->date_created_tz->time_zone->name, 'floating', "timestamp with time zone - $db_type");
94  is($o->timestamp_tz2->time_zone->name, 'Antarctica/Vostok', "timestamp with time zone override - $db_type");
95
96  # Make sure we're not in the Antarctica/Vostok time zone or any other
97  # time zone with the same offset.
98  my $error;
99
100  TRY:
101  {
102    local $@;
103
104    eval
105    {
106      my $dt1 = DateTime->now(time_zone => 'local');
107      my $dt2 = $dt1->clone;
108      $dt2->set_time_zone('Antarctica/Vostok');
109      die "local is equivalent to Antarctica/Vostok"  if($dt1->iso8601 eq $dt2->iso8601);
110    };
111
112    $error = $@;
113  }
114
115  if($error)
116  {
117    SKIP: { skip("timestamp with time zone time change - $db_type", 2) }
118  }
119  else
120  {
121    isnt($o->date_created_tz->iso8601, $o->timestamp_tz2->iso8601, "timestamp with time zone time change - $db_type");
122
123    $o->save;
124    $o->load;
125
126    my $dt = $o->timestamp_tz2->clone;
127    $dt->set_time_zone($o->date_created_tz->time_zone);
128
129    is($o->date_created_tz->iso8601, $dt->iso8601, "timestamp with time zone time change 2 - $db_type");
130  }
131
132  $o->name('C' x 50);
133  is($o->name, 'C' x 32, "varchar truncation - $db_type");
134
135  $o->name('John');
136
137  $o->code('A');
138  is($o->code, 'A     ', "character padding - $db_type");
139
140  $o->code('C' x 50);
141  is($o->code, 'C' x 6, "character truncation - $db_type");
142
143  my $ouk;
144  ok($ouk = MyPgObject->new(k1 => 1,
145                            k2 => undef,
146                            k3 => 3)->load, "load() uk 1 - $db_type");
147
148  ok(!$ouk->not_found, "not_found() uk 1 - $db_type");
149
150  is($ouk->id, 1, "load() uk 2 - $db_type");
151  is($ouk->name, 'John', "load() uk 3 - $db_type");
152
153  ok($ouk->save, "save() uk 1 - $db_type");
154
155  my $o2 = MyPgObject->new(id => $o->id);
156
157  ok(ref $o2 && $o2->isa('MyPgObject'), "new() 2 - $db_type");
158
159  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");
160
161  ok($o2->load, "load() 2 - $db_type");
162  ok(!$o2->not_found, "not_found() 1 - $db_type");
163
164  is($o2->name, $o->name, "load() verify 1 - $db_type");
165  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
166  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
167  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
168  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
169  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
170  is($o2->save_col, 7, "load() verify 7 (aliased column) - $db_type");
171  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");
172
173  $o2->flag2(undef);
174  $o2->save;
175
176  is($o2->flag2, undef, "boolean null - $db_type");
177
178  $o2->set_status('foo');
179  is($o2->get_status, 'foo', "get_status() - $db_type");
180  $o2->set_status('active');
181  eval { $o2->set_status };
182  ok($@, "set_status() - $db_type");
183
184  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");
185
186  my $clone = $o2->clone;
187  ok($o2->start eq $clone->start, "clone() 1 - $db_type");
188  $clone->start->set(year => '1960');
189  ok($o2->start ne $clone->start, "clone() 2 - $db_type");
190
191  $o2->start('5/24/2001');
192
193  sleep(1); # keep the last modified dates from being the same
194
195  $o2->last_modified('now');
196  ok($o2->save, "save() 2 - $db_type");
197  ok($o2->load, "load() 3 - $db_type");
198
199  ok(!has_modified_columns($o2), "no modified columns after load() - $db_type");
200
201  $o2->name('John 2');
202  $o2->save(changes_only => 1);
203
204  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
205  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
206  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");
207
208  my $bo = MyPgObject->new(id => $o->id);
209  $bo->load;
210  $bo->flag(0);
211  $bo->save;
212
213  $bo = MyPgObject->new(id => $o->id);
214  $bo->load;
215
216  ok(!$bo->flag, "boolean check - $db_type");
217
218  $bo->flag(0);
219  $bo->save;
220
221  my $o3 = MyPgObject->new();
222
223  my $db = $o3->db or die $o3->error;
224
225  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");
226
227  is($db->dbh, $o3->dbh, "dbh() - $db_type");
228
229  my $o4 = MyPgObject->new(id => 999);
230  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
231  ok($o4->not_found, "not_found() 2 - $db_type");
232
233  ok($o->load, "load() 4 - $db_type");
234
235  SKIP:
236  {
237    if($PG_HAS_CHKPASS)
238    {
239      $o->{'password_encrypted'} = ':8R1Kf2nOS0bRE';
240
241      ok($o->password_is('xyzzy'), "chkpass() 1 - $db_type");
242      is($o->password, 'xyzzy', "chkpass() 2 - $db_type");
243
244      $o->password('foobar');
245
246      ok($o->password_is('foobar'), "chkpass() 3 - $db_type");
247      is($o->password, 'foobar', "chkpass() 4 - $db_type");
248
249      $o->code('C1');
250      #local $Rose::DB::Object::Debug = 1;
251      ok($o->save, "save() 3 - $db_type");
252
253      $o = MyPgObject->new(id => $o->id)->load;
254      $o->code('C2');
255      $o->save;
256
257      $o = MyPgObject->new(id => $o->id)->load;
258      ok($o->password_is('foobar'), "chkpass() 6 - $db_type");
259    }
260    else
261    {
262      skip("chkpass tests", 6);
263    }
264  }
265
266  my $o5 = MyPgObject->new(id => $o->id);
267
268  ok($o5->load, "load() 5 - $db_type");
269
270  SKIP:
271  {
272    if($PG_HAS_CHKPASS)
273    {
274      ok($o5->password_is('foobar'), "chkpass() 7 - $db_type");
275      is($o5->password, 'foobar', "chkpass() 8 - $db_type");
276    }
277    else
278    {
279      skip("chkpass tests", 2);
280    }
281  }
282
283  $o5->nums([ 4, 5, 6 ]);
284  ok($o5->save, "save() 4 - $db_type");
285  ok($o->load, "load() 6 - $db_type");
286
287  is($o5->nums->[0], 4, "load() verify 10 (array value) - $db_type");
288  is($o5->nums->[1], 5, "load() verify 11 (array value) - $db_type");
289  is($o5->nums->[2], 6, "load() verify 12 (array value) - $db_type");
290
291  my @a = $o5->nums;
292
293  is($a[0], 4, "load() verify 13 (array value) - $db_type");
294  is($a[1], 5, "load() verify 14 (array value) - $db_type");
295  is($a[2], 6, "load() verify 15 (array value) - $db_type");
296  is(@a, 3, "load() verify 16 (array value) - $db_type");
297
298  ok($o->delete, "delete() - $db_type");
299
300  $o = MyPgObject->new(name => 'John', id => 9);
301  $o->save_col(22);
302  ok($o->save, "save() 4 - $db_type");
303  $o->save_col(50);
304  ok($o->save, "save() 5 - $db_type");
305
306  $ouk = MyPgObject->new(save_col => 50);
307  ok($ouk->load, "load() aliased unique key - $db_type");
308
309  eval { $o->meta->alias_column(nonesuch => 'foo') };
310  ok($@, "alias_column() nonesuch - $db_type");
311
312  # This is okay now
313  #eval { $o->meta->alias_column(id => 'foo') };
314  #ok($@, "alias_column() primary key - $db_type");
315
316  $o = MyPgObject->new(id => 777);
317
318  $o->meta->error_mode('fatal');
319
320  $o->dbh->{'PrintError'} = 0;
321
322  eval { $o->load };
323  ok($@ && $o->not_found, "load() not found fatal - $db_type");
324
325  $o->id('abc');
326
327  eval { $o->load };
328  ok($@ && !$o->not_found, "load() fatal - $db_type");
329
330  eval { $o->save };
331  ok($@, "save() fatal - $db_type");
332
333  $o = MyPgObject->new(id => 9999); # no such id
334
335  $o->meta->error_mode('fatal');
336
337  eval { $o->load() };
338  ok($@, "load() non-speculative implicit - $db_type");
339  ok(!$o->load(speculative => 1), "load() speculative explicit 1 - $db_type");
340  eval { $o->load(speculative => 0) };
341  ok($@, "load() non-speculative explicit 2 - $db_type");
342
343  $o->meta->default_load_speculative(1);
344
345  ok(!$o->load(), "load() speculative implicit - $db_type");
346  ok(!$o->load(speculative => 1), "load() speculative explicit 2 - $db_type");
347  eval { $o->load(speculative => 0) };
348  ok($@, "load() non-speculative explicit 2 - $db_type");
349
350  # Reset for next trip through loop
351  $o->meta->default_load_speculative(0);
352  $o->meta->error_mode('return');
353
354  $o = MyPgObject->new(name => 'John',
355                       k1   => 1,
356                       k2   => undef,
357                       k3   => 3)->save;
358
359  is($o->dur->months, 2, "interval months 1 - $db_type");
360  is($o->dur->days, 5, "interval days 1 - $db_type");
361  is($o->dur->seconds, 3, "interval seconds 1 - $db_type");
362
363  $o->dur(DateTime::Duration->new(years => 7, nanoseconds => 3000));
364
365  is($o->dur->in_units('years'), 7, "interval in_units years 1 - $db_type");
366  is($o->dur->in_units('months'), 84, "interval in_units months 1 - $db_type");
367  is($o->dur->nanoseconds, 3000, "interval nanoseconds 1 - $db_type");
368  is($o->dur->days, 0, "interval days 2 - $db_type");
369  is($o->dur->minutes, 0, "interval minutes 2 - $db_type");
370  is($o->dur->seconds, 0, "interval seconds 2 - $db_type");
371
372  $o->save;
373
374  # Select for update tests
375  $o = MyPgObject->new(id => $o->id);
376
377  $o->db->begin_work;
378  $o->load(for_update => 1);
379
380  # Silence errors in eval blocks below
381  Rose::DB->modify_db(type => $db_type)->print_error(0);
382
383  my $lo;
384
385  eval
386  {
387    $lo = MyPgObject->new(id => $o->id);
388    $lo->meta->error_mode('fatal');
389    $lo->load(lock => { for_update => 1, nowait => 1 });
390  };
391
392  is(DBI->err, 7, "select for update wait 1 error 7 - $db_type");
393  ok($@, "select for update no wait - $db_type");
394
395  $o->db->commit;
396
397  Rose::DB->modify_db(type => $db_type)->print_error(1);
398
399  $lo = MyPgObject->new(id => $o->id);
400  $lo->load(lock => { type => 'shared' });
401
402  $o = MyPgObject->new(id => $o->id)->load;
403
404  is($o->dur->in_units('years'), 7, "interval in_units years 2 - $db_type");
405  is($o->dur->in_units('months'), 84, "interval in_units months 2 - $db_type");
406  is($o->dur->nanoseconds, 3000, "interval nanoseconds 2 - $db_type");
407  is($o->dur->days, 0, "interval days 3 - $db_type");
408  is($o->dur->minutes, 0, "interval minutes 3 - $db_type");
409  is($o->dur->seconds, 0, "interval seconds 3 - $db_type");
410
411  is($o->epoch(format => '%Y-%m-%d %H:%M:%S'), '1999-11-30 21:30:00', "epoch 1 - $db_type");
412
413  $o->hiepoch('943997400.123456');
414  is($o->hiepoch(format => '%Y-%m-%d %H:%M:%S.%6N'), '1999-11-30 21:30:00.123456', "epoch hires 1 - $db_type");
415
416  $o->epoch('5/6/1980 12:34:56');
417
418  $o->save;
419
420  $o = MyPgObject->new(id => $o->id)->load;
421
422  is($o->epoch(format => '%Y-%m-%d %H:%M:%S'), '1980-05-06 12:34:56', "epoch 2 - $db_type");
423  is($o->hiepoch(format => '%Y-%m-%d %H:%M:%S.%6N'), '1999-11-30 21:30:00.123456', "epoch hires 2 - $db_type");
424
425  is($o->bint1, '9223372036854775800', "bigint 1 - $db_type");
426  is($o->bint2, '-9223372036854775800', "bigint 2 - $db_type");
427  is($o->bint3, '9223372036854775000', "bigint 3 - $db_type");
428  is($o->bint4, undef, "bigint null 1 - $db_type");
429
430  $o->bint4(555);
431  $o->bint1($o->bint1 + 1);
432  $o->save;
433
434  $o = MyPgObject->new(id => $o->id)->load;
435  is($o->bint1, '9223372036854775801', "bigint 4 - $db_type");
436  is($o->bint4, 555, "bigint null 2 - $db_type");
437
438  $o->bint4(undef);
439
440  $o->bint3(5);
441  eval { $o->bint3(7) };
442  ok($@, "bigint 5 - $db_type");
443
444  is($o->tee_time5->as_string, '12:34:56.12345', "time(5) - $db_type");
445
446  $o->tee_time0('1pm');
447  $o->tee_time('allballs');
448  $o->tee_time9('now');
449  $o->save;
450
451  $o =  MyPgObject->new(id => $o->id)->load;
452  is($o->tee_time->as_string, '00:00:00', "time allballs - $db_type");
453  ok($o->tee_time9->as_string =~ /^\d\d:\d\d:\d\d\.\d{1,6}$/, "time now - $db_type");
454  is($o->bint4, undef, "bigint null 3 - $db_type");
455
456  $o->tee_time(Time::Clock->new->parse('6:30 PM'));
457  $o->save;
458
459  $o =  MyPgObject->new(id => $o->id)->load;
460  is($o->tee_time->as_string, '18:30:00', "time 6:30 PM - $db_type");
461}
462
463#
464# MySQL
465#
466
467SKIP: foreach my $db_type ('mysql')
468{
469  skip("MySQL tests", 121)  unless($HAVE_MYSQL);
470
471  Rose::DB->default_type($db_type);
472
473  my $o = MyMySQLObject->new(name => 'John',
474                             k1   => 1,
475                             k2   => undef,
476                             k3   => 3);
477
478  # Checking to see that Perl code generation methods don't die (See: 0.767 changes)
479  $o->meta->column('name')->check_in([ qw(a b c) ]);
480  $o->meta->perl_class_definition;
481  $o->meta->column('name')->check_in(undef);
482
483  ok(ref $o && $o->isa('MyMySQLObject'), "new() 1 - $db_type");
484
485  $o->flag2('true');
486  $o->date_created('now');
487  $o->last_modified($o->date_created);
488  $o->save_col(22);
489
490  $o->bitz3('11');
491
492  if(rand >= 0.5)
493  {
494    ok($o->save, "save() 1 - $db_type");
495  }
496  else
497  {
498    ok($o->insert, "insert() 1 - $db_type");
499  }
500
501  # Select for update tests
502  if($INNODB && $ENV{'RDBO_SLOW_TESTS'})
503  {
504    $o = MyMySQLObject->new(id => $o->id);
505
506    $o->db->begin_work;
507    $o->load(for_update => 1);
508
509    # Silence errors in eval blocks below
510    Rose::DB->modify_db(type => $db_type)->print_error(0);
511
512    my $lo;
513
514    eval
515    {
516      $lo = MyMySQLObject->new(id => $o->id);
517      $lo->meta->error_mode('fatal');
518      $lo->load(lock => { for_update => 1 });
519    };
520
521    is(DBI->err, 1205, "select for update wait 1 error 1205 - $db_type");
522    ok($@, "select for update - $db_type");
523
524    $o->db->commit;
525  }
526  else
527  {
528    if($INNODB)
529    {
530      SKIP: { skip("Select for update tests: RDBO_SLOW_TESTS not set - $db_type", 2) }
531    }
532    else
533    {
534      SKIP: { skip("Select for update tests: no InnoDB - $db_type", 2) }
535    }
536  }
537
538  $o = MyMySQLObject->new(id => $o->id);
539  $o->load(lock => { type => 'shared' });
540
541  ok($o->load, "load() 1 - $db_type");
542
543  is(ref $o->dt_default, 'DateTime', "now() default - $db_type");
544
545  is($o->zepoch->ymd, '1970-01-01', "zero epoch default - $db_type");
546
547  is_deeply([ sort $o->items ], [ qw(a c) ], "set default - $db_type");
548
549  my $os = MyMySQLObject->new(id => $o->id)->load;
550  $os->items;
551
552  CATCH_STDERR:
553  {
554    local *STDERR;
555    my $stderr;
556    open(STDERR, '>', \$stderr) or die "Could not redirect STDERR - $!";
557
558    local $Rose::DB::Object::Debug = 1;
559    $os->save(changes_only => 1);
560    is($stderr, undef, "save changes only for set column - $db_type");
561  }
562
563  my $ox = MyMySQLObject->new(id => $o->id)->load;
564  is($ox->bitz2->to_Bin(), '00', "spot check bitfield 1 - $db_type");
565  is($ox->bitz3->to_Bin(), '0011', "spot check bitfield 2 - $db_type");
566
567  eval { $o->name('C' x 50) };
568  ok($@, "varchar overflow fatal - $db_type");
569
570  $o->name('John');
571
572  $o->code('A');
573  is($o->code, 'A     ', "character padding - $db_type");
574
575  eval { $o->code('C' x 50) };
576  ok($@, "code overflow fatal - $db_type");
577  $o->code('C' x 6);
578
579  is($o->enums, 'foo', "enum 1 - $db_type");
580  eval { $o->enums('blee') };
581  ok($@, "enum 2 - $db_type");
582
583  $o->enums('bar');
584
585  my $ouk;
586
587  ok($ouk = MyMySQLObject->new(k1 => 1,
588                               k2 => undef,
589                               k3 => 3)->load, "load() uk 1 - $db_type");
590
591  ok(!$ouk->not_found, "not_found() uk 1 - $db_type");
592
593  is($ouk->id, 1, "load() uk 2 - $db_type");
594  is($ouk->name, 'John', "load() uk 3 - $db_type");
595
596  ok($ouk->save, "save() uk 1 - $db_type");
597
598  my $o2 = MyMySQLObject->new(id => $o->id);
599
600  ok(ref $o2 && $o2->isa('MyMySQLObject'), "new() 2 - $db_type");
601
602  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");
603  is($o2->bitz2->to_Bin, '00', "bitz2() (bitfield default value) - $db_type");
604
605  ok($o2->load, "load() 2 - $db_type");
606
607  ok(!$o2->not_found, "not_found() 1 - $db_type");
608
609  is($o2->name, $o->name, "load() verify 1 - $db_type");
610  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
611  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
612  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
613  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
614  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
615  is($o2->save_col, 22, "load() verify 7 (aliased column) - $db_type");
616  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");
617
618  $o2->set_status('foo');
619  is($o2->get_status, 'foo', 'get_status()');
620  $o2->set_status('active');
621  eval { $o2->set_status };
622  ok($@, 'set_status()');
623
624  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");
625  is($o2->bitz2->to_Bin, '00', "load() verify 10 (bitfield value) - $db_type");
626  is($o2->bitz3->to_Bin, '0011', "load() verify 11 (bitfield value) - $db_type");
627
628  my $clone = $o2->clone;
629  ok($o2->start eq $clone->start, "clone() 1 - $db_type");
630  $clone->start->set(year => '1960');
631  ok($o2->start ne $clone->start, "clone() 2 - $db_type");
632
633  $o2->name('John 2');
634  $o2->start('5/24/2001');
635
636  sleep(1); # keep the last modified dates from being the same
637
638  $o2->last_modified('now');
639  ok($o2->save, "save() 2 - $db_type");
640  ok($o2->load, "load() 3 - $db_type");
641
642  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
643  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
644  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");
645
646  my $o3 = MyMySQLObject->new();
647
648  my $db = $o3->db or die $o3->error;
649
650  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");
651
652  is($db->dbh, $o3->dbh, "dbh() - $db_type");
653
654  my $o4 = MyMySQLObject->new(id => 999);
655  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
656  ok($o4->not_found, "not_found() 2 - $db_type");
657
658  eval { $o->items('z') };
659
660  ok($@ =~ /Invalid value/, "set invalid value - $db_type");
661
662
663  $o->items('a', 'b');
664  $o->nums([ 4, 5, 6 ]);
665
666  ok($o->save, "save() 3 - $db_type");
667  ok($o->load, "load() 4 - $db_type");
668
669  is_deeply([ sort $o->items ], [ qw(a b) ], "set default - $db_type");
670
671  is($o->nums->[0], 4, "load() verify 10 (array value) - $db_type");
672  is($o->nums->[1], 5, "load() verify 11 (array value) - $db_type");
673  is($o->nums->[2], 6, "load() verify 12 (array value) - $db_type");
674
675  my @a = $o->nums;
676
677  is($a[0], 4, "load() verify 13 (array value) - $db_type");
678  is($a[1], 5, "load() verify 14 (array value) - $db_type");
679  is($a[2], 6, "load() verify 15 (array value) - $db_type");
680  is(@a, 3, "load() verify 16 (array value) - $db_type");
681
682  ok($o->delete, "delete() - $db_type");
683
684  $o = MyMySQLObject->new(name => 'John', id => 9);
685  $o->save_col(22);
686
687  ok($o->save, "save() 4 - $db_type");
688  $o->save_col(50);
689  ok($o->save, "save() 5 - $db_type");
690
691  $ouk = MyMySQLObject->new(save_col => 50);
692  ok($ouk->load, "load() aliased unique key - $db_type");
693
694  eval { $o->meta->alias_column(nonesuch => 'foo') };
695  ok($@, "alias_column() nonesuch - $db_type");
696
697  # This is okay now
698  #eval { $o->meta->alias_column(id => 'foo') };
699  #ok($@, "alias_column() primary key - $db_type");
700
701  $o = MyMySQLObject->new(id => 777);
702
703  $o->meta->error_mode('fatal');
704
705  $o->dbh->{'PrintError'} = 0;
706
707  eval { $o->load };
708  ok($@ && $o->not_found, "load() not found fatal - $db_type");
709
710  my $old_table = $o->meta->table;
711
712  $o->meta->table('nonesuch');
713
714  eval { $o->load };
715  ok($@ && !$o->not_found, "load() fatal - $db_type");
716
717  eval { $o->save };
718  ok($@, "save() fatal - $db_type");
719
720  $o->meta->table($old_table);
721  $o->meta->error_mode('return');
722
723  $o = MyMPKMySQLObject->new(name => 'John');
724
725  ok($o->save, "save() 1 multi-value primary key with generated values - $db_type");
726
727  is($o->k1, 1, "save() verify 1 multi-value primary key with generated values - $db_type");
728  is($o->k2, 2, "save() verify 2 multi-value primary key with generated values - $db_type");
729
730  $o = MyMPKMySQLObject->new(name => 'Alex');
731
732  ok($o->save, "save() 2 multi-value primary key with generated values - $db_type");
733
734  is($o->k1, 3, "save() verify 3 multi-value primary key with generated values - $db_type");
735  is($o->k2, 4, "save() verify 4 multi-value primary key with generated values - $db_type");
736
737  is($ox->bitz3->to_Bin(), '0011', "spot check bitfield 3 - $db_type");
738
739  $ox->bitz3->Bit_On(3);
740  is($ox->bitz3->to_Bin(), '1011', "spot check bitfield 4 - $db_type");
741
742  $ox->save(insert => 1);
743
744  $ox = MyMySQLObject->new(id => $ox->id)->load;
745  is($ox->bitz3->to_Bin(), '1011', "spot check bitfield 5 - $db_type");
746
747  $ox->bitz3->Bit_On(2);
748  $ox->save;
749  $ox = MyMySQLObject->new(id => $ox->id)->load;
750  is($ox->bitz3->to_Bin(), '1111', "spot check bitfield 6 - $db_type");
751
752  $o = MyMySQLObject->new(id => 9999); # no such id
753
754  $o->meta->error_mode('fatal');
755
756  eval { $o->load() };
757  ok($@, "load() non-speculative implicit - $db_type");
758  ok(!$o->load(speculative => 1), "load() speculative explicit 1 - $db_type");
759  eval { $o->load(speculative => 0) };
760  ok($@, "load() non-speculative explicit 2 - $db_type");
761
762  $o->meta->default_load_speculative(1);
763
764  ok(!$o->load(), "load() speculative implicit - $db_type");
765  ok(!$o->load(speculative => 1), "load() speculative explicit 2 - $db_type");
766  eval { $o->load(speculative => 0) };
767  ok($@, "load() non-speculative explicit 2 - $db_type");
768
769  $o->meta->default_load_speculative(0);
770
771  $o = MyMySQLObject->new(id => 1)->load;
772
773  is($o->dur->months, 2, "interval months 1 - $db_type");
774  is($o->dur->days, 5, "interval days 1 - $db_type");
775  is($o->dur->seconds, 3, "interval seconds 1 - $db_type");
776
777  $o->dur(DateTime::Duration->new(years => 7, nanoseconds => 3000));
778
779  is($o->dur->in_units('years'), 7, "interval in_units years 1 - $db_type");
780  is($o->dur->in_units('months'), 84, "interval in_units months 1 - $db_type");
781  is($o->dur->nanoseconds, 3000, "interval nanoseconds 1 - $db_type");
782  is($o->dur->days, 0, "interval days 2 - $db_type");
783  is($o->dur->minutes, 0, "interval minutes 2 - $db_type");
784  is($o->dur->seconds, 0, "interval seconds 2 - $db_type");
785
786  $o->save;
787
788  $o = MyMySQLObject->new(id => $o->id)->load;
789
790  is($o->dur->in_units('years'), 7, "interval in_units years 2 - $db_type");
791  is($o->dur->in_units('months'), 84, "interval in_units months 2 - $db_type");
792  is($o->dur->nanoseconds, 3000, "interval nanoseconds 2 - $db_type");
793  is($o->dur->days, 0, "interval days 3 - $db_type");
794  is($o->dur->minutes, 0, "interval minutes 3 - $db_type");
795  is($o->dur->seconds, 0, "interval seconds 3 - $db_type");
796
797  is($o->meta->column('dur')->scale, 6, "interval scale - $db_type");
798
799  is($o->epoch(format => '%Y-%m-%d %H:%M:%S'), '1999-11-30 21:30:00', "epoch 1 - $db_type");
800
801  $o->hiepoch('943997400.123456');
802  is($o->hiepoch(format => '%Y-%m-%d %H:%M:%S.%6N'), '1999-11-30 21:30:00.123456', "epoch hires 1 - $db_type");
803
804  $o->epoch('5/6/1980 12:34:56');
805
806  $o->save;
807
808  $o = MyMySQLObject->new(id => $o->id)->load;
809
810  is($o->epoch(format => '%Y-%m-%d %H:%M:%S'), '1980-05-06 12:34:56', "epoch 2 - $db_type");
811  is($o->hiepoch(format => '%Y-%m-%d %H:%M:%S.%6N'), '1999-11-30 21:30:00.123456', "epoch hires 2 - $db_type");
812
813  is($o->tee_time5->as_string, '12:34:56.12345', "time(5) - $db_type");
814
815  $o->tee_time0('1pm');
816  eval { $o->tee_time('allballs') };
817  ok($@, "allballs - $db_type");
818  $o->tee_time('0:00');
819  $o->tee_time9('now');
820  $o->save;
821
822  $o = MyMySQLObject->new(id => $o->id)->load;
823  is($o->tee_time->as_string, '00:00:00', "time allballs - $db_type");
824
825  if($Have_HiRes_Time)
826  {
827    ok($o->tee_time9->as_string =~ /^\d\d:\d\d:\d\d\.\d+$/, "time now - $db_type");
828  }
829  else
830  {
831    ok($o->tee_time9->as_string =~ /^\d\d:\d\d:\d\d$/, "time now - $db_type");
832  }
833
834  $o->tee_time(Time::Clock->new->parse('6:30 PM'));
835  $o->save;
836
837  $o = MyMySQLObject->new(id => $o->id)->load;
838  is($o->tee_time->as_string, '18:30:00', "time 6:30 PM - $db_type");
839
840  MyMySQLObject->meta->column('save')->default('x');
841  MyMySQLObject->meta->make_column_methods(replace_existing => 1);
842
843  $o->meta->default_load_speculative(0);
844
845  $o = MyMySQLObject->new(k1 => 1, k3 => 3);
846  ok(!$o->load(speculative => 1), "load default key - $db_type");
847
848  eval { $o->load(use_key => 'id') };
849  ok($@, "use_key no such key - $db_type");
850
851  $o->load(use_key => 'k1_k2_k3');
852  is($o->k1, 1, "load specific key 1 - $db_type");
853  is($o->k3, 3, "load specific key 2 - $db_type");
854  is($o->name, 'John', "load specific key 3 - $db_type");
855}
856
857#
858# Informix
859#
860
861SKIP: foreach my $db_type ('informix')
862{
863  skip("Informix tests", 73)  unless($HAVE_INFORMIX);
864
865  Rose::DB->default_type($db_type);
866
867  my $o = MyInformixObject->new(name => 'John',
868                                id   => 1,
869                                k1   => 1,
870                                k2   => undef,
871                                k3   => 3);
872
873  ok(ref $o && $o->isa('MyInformixObject'), "new() 1 - $db_type");
874
875  $o->meta->allow_inline_column_values(1);
876
877  $o->flag2('true');
878  $o->date_created('current year to fraction(5)');
879  $o->last_modified($o->date_created);
880  $o->save_col(22);
881
882  if(rand >= 0.5)
883  {
884    ok($o->save, "save() 1 - $db_type");
885  }
886  else
887  {
888    ok($o->insert, "insert() 1 - $db_type");
889  }
890
891  ok($o->load, "load() 1 - $db_type");
892
893  $o->name('C' x 50);
894  is($o->name, 'C' x 32, "varchar truncation - $db_type");
895
896  $o->name('John');
897
898  $o->code('A');
899  is($o->code, 'A     ', "character padding - $db_type");
900
901  $o->code('C' x 50);
902  is($o->code, 'C' x 6, "character truncation - $db_type");
903
904  my $ouk;
905  ok($ouk = MyInformixObject->new(k1 => 1,
906                                  k2 => undef,
907                                  k3 => 3)->load, "load() uk 1 - $db_type");
908
909  ok(!$ouk->not_found, "not_found() uk 1 - $db_type");
910
911  is($ouk->id, 1, "load() uk 2 - $db_type");
912  is($ouk->name, 'John', "load() uk 3 - $db_type");
913
914  ok($ouk->save, "save() uk 1 - $db_type");
915
916  my $o2 = MyInformixObject->new(id => $o->id);
917
918  ok(ref $o2 && $o2->isa('MyInformixObject'), "new() 2 - $db_type");
919
920  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");
921
922  ok($o2->load, "load() 2 - $db_type");
923  ok(!$o2->not_found, "not_found() 1 - $db_type");
924
925  is($o2->name, $o->name, "load() verify 1 - $db_type");
926  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
927  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
928  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
929  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
930  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
931  is($o2->save_col, 22, "load() verify 7 (aliased column) - $db_type");
932  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");
933
934  $o2->set_status('foo');
935  is($o2->get_status, 'foo', 'get_status()');
936  $o2->set_status('active');
937  eval { $o2->set_status };
938  ok($@, 'set_status()');
939
940  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");
941
942  my $clone = $o2->clone;
943  ok($o2->start eq $clone->start, "clone() 1 - $db_type");
944  $clone->start->set(year => '1960');
945  ok($o2->start ne $clone->start, "clone() 2 - $db_type");
946
947  $o2->name('John 2');
948  $o2->start('5/24/2001');
949
950  sleep(1); # keep the last modified dates from being the same
951
952  $o2->last_modified('current year to second');
953  ok($o2->save, "save() 2 - $db_type");
954  ok($o2->load, "load() 3 - $db_type");
955
956  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
957  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
958  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");
959
960  my $o3 = MyInformixObject->new();
961
962  my $db = $o3->db or die $o3->error;
963
964  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");
965
966  is($db->dbh, $o3->dbh, "dbh() - $db_type");
967
968  my $o4 = MyInformixObject->new(id => 999);
969  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
970  ok($o4->not_found, "not_found() 2 - $db_type");
971
972  $o->nums([ 4, 5, 6 ]);
973  $o->names([ qw(a b 3.1) ]);
974
975  ok($o->save, "save() 3 - $db_type");
976  ok($o->load, "load() 4 - $db_type");
977
978  is($o->nums->[0], 4, "load() verify 10 (array value) - $db_type");
979  is($o->nums->[1], 5, "load() verify 11 (array value) - $db_type");
980  is($o->nums->[2], 6, "load() verify 12 (array value) - $db_type");
981
982  $o->nums(7, 8, 9);
983
984  my @a = $o->nums;
985
986  is($a[0], 7, "load() verify 13 (array value) - $db_type");
987  is($a[1], 8, "load() verify 14 (array value) - $db_type");
988  is($a[2], 9, "load() verify 15 (array value) - $db_type");
989  is(@a, 3, "load() verify 16 (array value) - $db_type");
990
991  is($o->names->[0], 'a', "load() verify 10 (set value) - $db_type");
992  is($o->names->[1], 'b', "load() verify 11 (set value) - $db_type");
993  is($o->names->[2], '3.1', "load() verify 12 (set value) - $db_type");
994
995  $o->names('c', 'd', '4.2');
996
997  @a = $o->names;
998
999  is($a[0], 'c', "load() verify 13 (set value) - $db_type");
1000  is($a[1], 'd', "load() verify 14 (set value) - $db_type");
1001  is($a[2], '4.2', "load() verify 15 (set value) - $db_type");
1002  is(@a, 3, "load() verify 16 (set value) - $db_type");
1003
1004  ok($o->delete, "delete() - $db_type");
1005
1006  $o = MyInformixObject->new(name => 'John', id => 9);
1007
1008  $o->flag2('true');
1009  $o->date_created('current year to fraction(5)');
1010  $o->last_modified($o->date_created);
1011  $o->save_col(22);
1012
1013  ok($o->save, "save() 4 - $db_type");
1014  $o->save_col(50);
1015
1016  ok($o->save, "save() 5 - $db_type");
1017
1018  $ouk = MyInformixObject->new(save_col => 50);
1019  ok($ouk->load, "load() aliased unique key - $db_type");
1020
1021  eval { $o->meta->alias_column(nonesuch => 'foo') };
1022  ok($@, "alias_column() nonesuch - $db_type");
1023
1024  # This is okay now
1025  #eval { $o->meta->alias_column(id => 'foo') };
1026  #ok($@, "alias_column() primary key - $db_type");
1027
1028  $o = MyInformixObject->new(id => 777);
1029
1030  $o->meta->error_mode('fatal');
1031
1032  $o->dbh->{'PrintError'} = 0;
1033
1034  eval { $o->load };
1035  ok($@ && $o->not_found, "load() not found fatal - $db_type");
1036
1037  $o->id('abc');
1038
1039  eval { $o->load };
1040  ok($@ && !$o->not_found, "load() fatal - $db_type");
1041
1042  eval { $o->save };
1043  ok($@, "save() fatal - $db_type");
1044
1045  #$o->meta->error_mode('return');
1046
1047  $o = MyInformixObject->new(id => 9999); # no such id
1048
1049  $o->meta->error_mode('fatal');
1050
1051  eval { $o->load() };
1052  ok($@, "load() non-speculative implicit - $db_type");
1053  ok(!$o->load(speculative => 1), "load() speculative explicit 1 - $db_type");
1054  eval { $o->load(speculative => 0) };
1055  ok($@, "load() non-speculative explicit 2 - $db_type");
1056
1057  $o->meta->default_load_speculative(1);
1058
1059  ok(!$o->load(), "load() speculative implicit - $db_type");
1060  ok(!$o->load(speculative => 1), "load() speculative explicit 2 - $db_type");
1061  eval { $o->load(speculative => 0) };
1062  ok($@, "load() non-speculative explicit 2 - $db_type");
1063}
1064
1065#
1066# SQLite
1067#
1068
1069SKIP: foreach my $db_type ('sqlite')
1070{
1071  skip("SQLite tests", 75)  unless($HAVE_SQLITE);
1072
1073  Rose::DB->default_type($db_type);
1074
1075  my $o = MySQLiteObject->new(name => 'John',
1076                              k1   => 1,
1077                              k2   => undef,
1078                              k3   => 3);
1079
1080  ok(ref $o && $o->isa('MySQLiteObject'), "new() 1 - $db_type");
1081
1082  $o->flag2('true');
1083  $o->date_created('now');
1084  $o->last_modified($o->date_created);
1085  $o->save_col(22);
1086
1087  if(rand >= 0.5)
1088  {
1089    ok($o->save, "save() 1 - $db_type");
1090  }
1091  else
1092  {
1093    ok($o->insert, "insert() 1 - $db_type");
1094  }
1095
1096  ok($o->load, "load() 1 - $db_type");
1097
1098  $o->name('C' x 50);
1099  is($o->name, 'C' x 32, "varchar truncation - $db_type");
1100
1101  $o->name('John');
1102
1103  $o->code('A');
1104  is($o->code, 'A     ', "character padding - $db_type");
1105
1106  $o->code('C' x 50);
1107  is($o->code, 'C' x 6, "character truncation - $db_type");
1108
1109  my $ouk;
1110  ok($ouk = MySQLiteObject->new(k1 => 1,
1111                                k2 => undef,
1112                                k3 => 3)->load, "load() uk 1 - $db_type");
1113
1114  ok(!$ouk->not_found, "not_found() uk 1 - $db_type");
1115
1116  is($ouk->id->[0], 1, "load() uk 2 - $db_type");
1117  is($ouk->name, 'John', "load() uk 3 - $db_type");
1118
1119  ok($ouk->save, "save() uk 1 - $db_type");
1120
1121  my $o2 = MySQLiteObject->new(id => $o->id);
1122
1123  ok(ref $o2 && $o2->isa('MySQLiteObject'), "new() 2 - $db_type");
1124
1125  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");
1126
1127  ok($o2->load, "load() 2 - $db_type");
1128  ok(!$o2->not_found, "not_found() 1 - $db_type");
1129
1130  is($o2->name, $o->name, "load() verify 1 - $db_type");
1131  is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
1132  is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
1133  is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
1134  is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
1135  is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
1136  is($o2->save_col, 22, "load() verify 7 (aliased column) - $db_type");
1137  is($o2->start->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");
1138
1139  $o2->set_status('foo');
1140  is($o2->get_status, 'foo', 'get_status()');
1141  $o2->set_status('active');
1142  eval { $o2->set_status };
1143  ok($@, 'set_status()');
1144
1145  is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");
1146
1147  my $clone = $o2->clone;
1148  ok($o2->start eq $clone->start, "clone() 1 - $db_type");
1149  $clone->start->set(year => '1960');
1150  ok($o2->start ne $clone->start, "clone() 2 - $db_type");
1151
1152  $o2->name('John 2');
1153  $o2->start('5/24/2001');
1154
1155  sleep(1); # keep the last modified dates from being the same
1156
1157  $o2->last_modified('now');
1158  ok($o2->save, "save() 2 - $db_type");
1159  ok($o2->load, "load() 3 - $db_type");
1160
1161  is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
1162  ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
1163  is($o2->start->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");
1164
1165  my $o3 = MySQLiteObject->new();
1166
1167  my $db = $o3->db or die $o3->error;
1168
1169  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");
1170
1171  is($db->dbh, $o3->dbh, "dbh() - $db_type");
1172
1173  my $o4 = MySQLiteObject->new(id => 999);
1174  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
1175  ok($o4->not_found, "not_found() 2 - $db_type");
1176
1177  $o->nums([ 4, 5, 6 ]);
1178  ok($o->save, "save() 3 - $db_type");
1179  ok($o->load, "load() 4 - $db_type");
1180
1181  is($o->nums->[0], 4, "load() verify 10 (array value) - $db_type");
1182  is($o->nums->[1], 5, "load() verify 11 (array value) - $db_type");
1183  is($o->nums->[2], 6, "load() verify 12 (array value) - $db_type");
1184
1185  my @a = $o->nums;
1186
1187  is($a[0], 4, "load() verify 13 (array value) - $db_type");
1188  is($a[1], 5, "load() verify 14 (array value) - $db_type");
1189  is($a[2], 6, "load() verify 15 (array value) - $db_type");
1190  is(@a, 3, "load() verify 16 (array value) - $db_type");
1191
1192  ok($o->delete, "delete() - $db_type");
1193
1194  $o = MySQLiteObject->new(name => 'John', id => 9);
1195  $o->save_col(22);
1196  ok($o->save, "save() 4 - $db_type");
1197  $o->save_col(50);
1198  ok($o->save, "save() 5 - $db_type");
1199
1200  $ouk = MySQLiteObject->new(save_col => 50);
1201  ok($ouk->load, "load() aliased unique key - $db_type");
1202
1203  eval { $o->meta->alias_column(nonesuch => 'foo') };
1204  ok($@, "alias_column() nonesuch - $db_type");
1205
1206  # This is okay now
1207  #eval { $o->meta->alias_column(id => 'foo') };
1208  #ok($@, "alias_column() primary key - $db_type");
1209
1210  $o = MySQLiteObject->new(id => 777);
1211
1212  $o->meta->error_mode('fatal');
1213
1214  $o->dbh->{'PrintError'} = 0;
1215
1216  eval { $o->load };
1217  ok($@ && $o->not_found, "load() not found fatal - $db_type");
1218
1219  my $old_table = $o->meta->table;
1220
1221  $o->meta->table('nonesuch');
1222
1223  eval { $o->load };
1224  ok($@ && !$o->not_found, "load() fatal - $db_type");
1225
1226  eval { $o->save };
1227  ok($@, "save() fatal - $db_type");
1228
1229  $o->meta->table($old_table);
1230  $o->meta->error_mode('return');
1231
1232  $o = MyMPKSQLiteObject->new(name => 'John');
1233
1234  ok($o->save, "save() 1 multi-value primary key with generated values - $db_type");
1235
1236  is($o->k1, 1, "save() verify 1 multi-value primary key with generated values - $db_type");
1237  is($o->k2, 2, "save() verify 2 multi-value primary key with generated values - $db_type");
1238
1239  $o = MyMPKSQLiteObject->new(name => 'Alex');
1240
1241  ok($o->save, "save() 2 multi-value primary key with generated values - $db_type");
1242
1243  is($o->k1, 3, "save() verify 3 multi-value primary key with generated values - $db_type");
1244  is($o->k2, 4, "save() verify 4 multi-value primary key with generated values - $db_type");
1245
1246  $o = MySQLiteObject->new(id => 9999); # no such id
1247
1248  $o->meta->error_mode('fatal');
1249
1250  eval { $o->load() };
1251  ok($@, "load() non-speculative implicit - $db_type");
1252  ok(!$o->load(speculative => 1), "load() speculative explicit 1 - $db_type");
1253  eval { $o->load(speculative => 0) };
1254  ok($@, "load() non-speculative explicit 2 - $db_type");
1255
1256  $o->meta->default_load_speculative(1);
1257
1258  ok(!$o->load(), "load() speculative implicit - $db_type");
1259  ok(!$o->load(speculative => 1), "load() speculative explicit 2 - $db_type");
1260  eval { $o->load(speculative => 0) };
1261  ok($@, "load() non-speculative explicit 2 - $db_type");
1262
1263  #
1264  # Test SQLite BLOB support
1265  #
1266
1267  my $blob = "abc\0def";
1268  $o = MySQLiteObject->new(id => 888, name => 'Blob', data => $blob);
1269  $o->save;
1270
1271  $o = MySQLiteObject->new(id => $o->id)->load;
1272  is($o->data, $blob, "blob check - $db_type");
1273}
1274
1275SKIP: foreach my $db_type (qw(oracle))
1276{
1277  skip("Oracle tests", 85)  unless($HAVE_ORACLE);
1278
1279  Rose::DB->default_type($db_type);
1280
1281  TEST_HACK:
1282  {
1283    no warnings;
1284    *MyOracleObject::init_db = sub { Rose::DB->new($db_type) };
1285  }
1286
1287  my $o = MyOracleObject->new(name => 'John',
1288                              k1   => 1,
1289                              k2   => undef,
1290                              k3   => 3);
1291
1292  ok(ref $o && $o->isa('MyOracleObject'), "new() 1 - $db_type");
1293
1294  $o->flag2('TRUE');
1295  $o->date_created('now');
1296  $o->date_created_tz('now');
1297  $o->timestamp_tz2('now');
1298  $o->last_modified($o->date_created);
1299  $o->save_col(7);
1300
1301  if(rand >= 0.5)
1302  {
1303    ok($o->save, "save() 1 - $db_type");
1304  }
1305  else
1306  {
1307    ok($o->insert, "insert() 1 - $db_type");
1308  }
1309
1310  is($o->meta->primary_key->sequence_names->[0], 'ROSE_DB_OBJECT_TEST_ID_SEQ',
1311     "pk sequence name - $db_type");
1312
1313  ok(is_in_db($o), "is_in_db - $db_type");
1314
1315  is($o->id, 1, "auto-generated primary key - $db_type");
1316
1317  if(oracle_is_broken())
1318  {
1319    SKIP: { skip("tests that trigger the dreaded ORA-00600 kpofdr-long error", 4) }
1320  }
1321  else
1322  {
1323    ok($o->load, "load() 1 - $db_type");
1324
1325    is($o->date_created->time_zone->name, 'floating', "timestamp without time zone - $db_type");
1326    isnt($o->date_created_tz->time_zone->name, 'floating', "timestamp with time zone - $db_type");
1327    is($o->timestamp_tz2->time_zone->name, 'Antarctica/Vostok', "timestamp with time zone override - $db_type");
1328
1329    # Make sure we're not in the Antarctica/Vostok time zone or any other
1330    # time zone with the same offset.
1331    my $error;
1332
1333    TRY:
1334    {
1335      local $@;
1336
1337      eval
1338      {
1339        my $dt1 = DateTime->now(time_zone => 'local');
1340        my $dt2 = $dt1->clone;
1341        $dt2->set_time_zone('Antarctica/Vostok');
1342        die "local is equivalent to Antarctica/Vostok"  if($dt1->iso8601 eq $dt2->iso8601);
1343      };
1344
1345      $error = $@;
1346    }
1347
1348    if($error)
1349    {
1350      SKIP: { skip("timestamp with time zone time change - $db_type", 2) }
1351    }
1352    else
1353    {
1354      isnt($o->date_created_tz->iso8601, $o->timestamp_tz2->iso8601, "timestamp with time zone time change - $db_type");
1355
1356      $o->save;
1357      $o->load;
1358
1359      my $dt = $o->timestamp_tz2->clone;
1360      $dt->set_time_zone($o->date_created_tz->time_zone);
1361
1362      is($o->date_created_tz->iso8601, $dt->iso8601, "timestamp with time zone time change 2 - $db_type");
1363    }
1364
1365    $o->name('C' x 50);
1366    is($o->name, 'C' x 32, "varchar truncation - $db_type");
1367
1368    $o->name('John');
1369
1370    $o->code('A');
1371    is($o->code, 'A     ', "character padding - $db_type");
1372
1373    $o->code('C' x 50);
1374    is($o->code, 'C' x 6, "character truncation - $db_type");
1375  }
1376
1377  my $ouk;
1378  ok($ouk = MyOracleObject->new(k1 => 1,
1379                                k2 => undef,
1380                                k3 => 3)->load, "load() uk 1 - $db_type");
1381
1382  ok(!$ouk->not_found, "not_found() uk 1 - $db_type");
1383
1384  is($ouk->id, 1, "load() uk 2 - $db_type");
1385  is($ouk->name, 'John', "load() uk 3 - $db_type");
1386
1387  ok($ouk->save, "save() uk 1 - $db_type");
1388
1389  my $o2 = MyOracleObject->new(id => $o->id);
1390
1391  ok(ref $o2 && $o2->isa('MyOracleObject'), "new() 2 - $db_type");
1392
1393  is($o2->bits->to_Bin, '00101', "bits() (bitfield default value) - $db_type");
1394
1395  if(oracle_is_broken())
1396  {
1397    SKIP: { skip("tests that trigger the dreaded ORA-00600 kpofdr-long error", 22) }
1398  }
1399  else
1400  {
1401    ok($o2->load, "load() 2 - $db_type");
1402    ok(!$o2->not_found, "not_found() 1 - $db_type");
1403
1404    is($o2->name, $o->name, "load() verify 1 - $db_type");
1405    is($o2->date_created, $o->date_created, "load() verify 2 - $db_type");
1406    is($o2->last_modified, $o->last_modified, "load() verify 3 - $db_type");
1407    is($o2->status, 'active', "load() verify 4 (default value) - $db_type");
1408    is($o2->flag, 1, "load() verify 5 (default boolean value) - $db_type");
1409    is($o2->flag2, 1, "load() verify 6 (boolean value) - $db_type");
1410    is($o2->save_col, 7, "load() verify 7 (aliased column) - $db_type");
1411    is($o2->start_date->ymd, '1980-12-24', "load() verify 8 (date value) - $db_type");
1412
1413    $o2->set_status('foo');
1414    is($o2->get_status, 'foo', "get_status() - $db_type");
1415    $o2->set_status('active');
1416    eval { $o2->set_status };
1417    ok($@, "set_status() - $db_type");
1418
1419    is($o2->bits->to_Bin, '00101', "load() verify 9 (bitfield value) - $db_type");
1420
1421    my $clone = $o2->clone;
1422    ok($o2->start_date eq $clone->start_date, "clone() 1 - $db_type");
1423    $clone->start_date->set(year => '1960');
1424    ok($o2->start_date ne $clone->start_date, "clone() 2 - $db_type");
1425
1426    $o2->start_date('5/24/2001');
1427
1428    sleep(1); # keep the last modified dates from being the same
1429
1430    $o2->last_modified('now');
1431    ok($o2->save, "save() 2 - $db_type");
1432    ok($o2->load, "load() 3 - $db_type");
1433
1434    ok(!has_modified_columns($o2), "no modified columns after load() - $db_type");
1435
1436    $o2->name('John 2');
1437    $o2->save(changes_only => 1);
1438
1439    is($o2->date_created, $o->date_created, "save() verify 1 - $db_type");
1440    ok($o2->last_modified ne $o->last_modified, "save() verify 2 - $db_type");
1441    is($o2->start_date->ymd, '2001-05-24', "save() verify 3 (date value) - $db_type");
1442
1443    my $bo = MyOracleObject->new(id => $o->id);
1444    $bo->load;
1445    $bo->flag(0);
1446    $bo->save;
1447
1448    $bo = MyOracleObject->new(id => $o->id);
1449    $bo->load;
1450
1451    ok(!$bo->flag, "boolean check - $db_type");
1452
1453    $bo->flag(0);
1454    $bo->save;
1455  }
1456
1457  my $o3 = MyOracleObject->new();
1458
1459  my $db = $o3->db or die $o3->error;
1460
1461  ok(ref $db && $db->isa('Rose::DB'), "db() - $db_type");
1462
1463  is($db->dbh, $o3->dbh, "dbh() - $db_type");
1464
1465  my $o4 = MyOracleObject->new(id => 999);
1466  ok(!$o4->load(speculative => 1), "load() nonexistent - $db_type");
1467  ok($o4->not_found, "not_found() 2 - $db_type");
1468
1469  ok($o->load, "load() 4 - $db_type");
1470
1471  my $o5 = MyOracleObject->new(id => $o->id);
1472
1473  ok($o5->load, "load() 5 - $db_type");
1474
1475  $o5->nums([ 4, 5, 6 ]);
1476  ok($o5->save, "save() 4 - $db_type");
1477  ok($o->load, "load() 6 - $db_type");
1478
1479  is($o5->nums->[0], 4, "load() verify 10 (array value) - $db_type");
1480  is($o5->nums->[1], 5, "load() verify 11 (array value) - $db_type");
1481  is($o5->nums->[2], 6, "load() verify 12 (array value) - $db_type");
1482
1483  my @a = $o5->nums;
1484
1485  is($a[0], 4, "load() verify 13 (array value) - $db_type");
1486  is($a[1], 5, "load() verify 14 (array value) - $db_type");
1487  is($a[2], 6, "load() verify 15 (array value) - $db_type");
1488  is(@a, 3, "load() verify 16 (array value) - $db_type");
1489
1490  ok($o->delete, "delete() - $db_type");
1491
1492  $o = MyOracleObject->new(name => 'John', id => 9);
1493  $o->save_col(22);
1494  ok($o->save, "save() 4 - $db_type");
1495  $o->save_col(50);
1496  ok($o->save, "save() 5 - $db_type");
1497
1498  $ouk = MyOracleObject->new(save_col => 50);
1499  ok($ouk->load, "load() aliased unique key - $db_type");
1500
1501  eval { $o->meta->alias_column(nonesuch => 'foo') };
1502  ok($@, "alias_column() nonesuch - $db_type");
1503
1504  # This is okay now
1505  #eval { $o->meta->alias_column(id => 'foo') };
1506  #ok($@, "alias_column() primary key - $db_type");
1507
1508  $o = MyOracleObject->new(id => 777);
1509
1510  $o->meta->error_mode('fatal');
1511
1512  $o->dbh->{'PrintError'} = 0;
1513
1514  eval { $o->load };
1515  ok($@ && $o->not_found, "load() not found fatal - $db_type");
1516
1517  $o->id('abc');
1518
1519  eval { $o->load };
1520  ok($@ && !$o->not_found, "load() fatal - $db_type");
1521
1522  eval { $o->save };
1523  ok($@, "save() fatal - $db_type");
1524
1525  $o = MyOracleObject->new(id => 9999); # no such id
1526
1527  $o->meta->error_mode('fatal');
1528
1529  eval { $o->load() };
1530  ok($@, "load() non-speculative implicit - $db_type");
1531  ok(!$o->load(speculative => 1), "load() speculative explicit 1 - $db_type");
1532  eval { $o->load(speculative => 0) };
1533  ok($@, "load() non-speculative explicit 2 - $db_type");
1534
1535  $o->meta->default_load_speculative(1);
1536
1537  ok(!$o->load(), "load() speculative implicit - $db_type");
1538  ok(!$o->load(speculative => 1), "load() speculative explicit 2 - $db_type");
1539  eval { $o->load(speculative => 0) };
1540  ok($@, "load() non-speculative explicit 2 - $db_type");
1541
1542  $o = MyOracleObject->new(name => 'Sequence Test',
1543                           k1   => 4,
1544                           k2   => 5,
1545                           k3   => 6,
1546                           key  => 123);
1547
1548  $o->save;
1549
1550  like($o->id, qr/^\d+$/, "save() serial - $db_type");
1551
1552  # Select for update tests
1553
1554  $o = MyOracleObject->new(id => $o->id)->load(for_update => 1, lock => { columns => [ qw(k2 k3) ] });
1555
1556  # Silence errors in eval blocks below
1557  Rose::DB->modify_db(type => $db_type)->print_error(0);
1558
1559  eval
1560  {
1561    $o =
1562      MyOracleObject->new(id => $o->id)->load(
1563        lock =>
1564        {
1565          type   => 'for update',
1566          on     => [ qw(k2 k3) ],
1567          nowait => 1,
1568        });
1569  };
1570
1571  ok($@, "select for update failure - $db_type");
1572
1573  my $lo;
1574
1575  eval
1576  {
1577    $lo = MyOracleObject->new(id => $o->id);
1578    $lo->load(lock => { for_update => 1, nowait => 1 });
1579  };
1580
1581  is(DBI->err, 54, "select for update no wait ORA-00054 - $db_type");
1582  ok($@, "select for update no wait - $db_type");
1583
1584  eval
1585  {
1586    $lo = MyOracleObject->new(id => $o->id);
1587    $lo->load(lock => { type => 'for update', wait => 1 });
1588  };
1589
1590  is(DBI->err, 30006, "select for update wait 1 ORA-30006 - $db_type");
1591  ok($@, "select for update wait 1 - $db_type");
1592
1593  $o->save;
1594
1595  Rose::DB->modify_db(type => $db_type)->print_error(1);
1596
1597  # Reset for next trip through loop (if any)
1598  $o->meta->default_load_speculative(0);
1599  $o->meta->error_mode('return');
1600
1601  $o = MyOracleObject->new(key => 123);
1602  eval { $o->load };
1603
1604  ok(!$@, "reserved-word load() - $db_type");
1605}
1606
1607BEGIN
1608{
1609  #
1610  # PostgreSQL
1611  #
1612
1613  my $dbh;
1614
1615  eval
1616  {
1617    $dbh = Rose::DB->new('pg_admin')->retain_dbh()
1618      or die Rose::DB->error;
1619  };
1620
1621  if(!$@ && $dbh)
1622  {
1623    our $HAVE_PG = 1;
1624
1625    # Drop existing table and create schema, ignoring errors
1626    {
1627      local $dbh->{'RaiseError'} = 0;
1628      local $dbh->{'PrintError'} = 0;
1629      $dbh->do('DROP TABLE rose_db_object_test');
1630      $dbh->do('DROP TABLE rose_db_object_private.rose_db_object_test');
1631      $dbh->do('DROP TABLE rose_db_object_chkpass_test');
1632      $dbh->do('CREATE SCHEMA rose_db_object_private');
1633    }
1634
1635    our $PG_HAS_CHKPASS = pg_has_chkpass();
1636
1637    $dbh->do(<<"EOF");
1638CREATE TABLE rose_db_object_test
1639(
1640  id              SERIAL NOT NULL PRIMARY KEY,
1641  k1              INT,
1642  k2              INT,
1643  k3              INT,
1644  @{[ $PG_HAS_CHKPASS ? 'passwd CHKPASS,' : '' ]}
1645  name            VARCHAR(32) NOT NULL,
1646  code            CHAR(6),
1647  flag            BOOLEAN NOT NULL,
1648  flag2           BOOLEAN,
1649  status          VARCHAR(32) DEFAULT 'active',
1650  bitz            BIT(5) NOT NULL DEFAULT B'00101',
1651  decs            DECIMAL(10,2),
1652  start           DATE,
1653  save            INT,
1654  nums            INT[],
1655  dur             INTERVAL(6) DEFAULT '2 months 5 days 3 seconds',
1656  epoch           INT DEFAULT 943997400,
1657  hiepoch         DECIMAL(16,6),
1658  bint1           BIGINT DEFAULT 9223372036854775800,
1659  bint2           BIGINT DEFAULT -9223372036854775800,
1660  bint3           BIGINT,
1661  bint4           BIGINT,
1662  tee_time        TIME,
1663  tee_time0       TIME(0),
1664  tee_time5       TIME(5),
1665  tee_time9       TIME(9),
1666  last_modified   TIMESTAMP,
1667  date_created    TIMESTAMP,
1668  date_created_tz TIMESTAMP WITH TIME ZONE,
1669  timestamp_tz2   TIMESTAMP WITH TIME ZONE,
1670
1671  UNIQUE(k1, k2, k3)
1672)
1673EOF
1674
1675    $dbh->do(<<"EOF");
1676CREATE TABLE rose_db_object_private.rose_db_object_test
1677(
1678  id              SERIAL NOT NULL PRIMARY KEY,
1679  k1              INT,
1680  k2              INT,
1681  k3              INT,
1682  @{[ $PG_HAS_CHKPASS ? 'passwd CHKPASS,' : '' ]}
1683  name            VARCHAR(32) NOT NULL,
1684  code            CHAR(6),
1685  flag            BOOLEAN NOT NULL,
1686  flag2           BOOLEAN,
1687  status          VARCHAR(32) DEFAULT 'active',
1688  bitz            BIT(5) NOT NULL DEFAULT B'00101',
1689  decs            DECIMAL(10,2),
1690  start           DATE,
1691  save            INT,
1692  nums            INT[],
1693  dur             INTERVAL(6) DEFAULT '2 months 5 days 3 seconds',
1694  epoch           INT DEFAULT 943997400,
1695  hiepoch         DECIMAL(16,6),
1696  bint1           BIGINT DEFAULT 9223372036854775800,
1697  bint2           BIGINT DEFAULT -9223372036854775800,
1698  bint3           BIGINT,
1699  bint4           BIGINT,
1700  tee_time        TIME,
1701  tee_time0       TIME(0),
1702  tee_time5       TIME(5),
1703  tee_time9       TIME(9),
1704  last_modified   TIMESTAMP,
1705  date_created    TIMESTAMP,
1706  date_created_tz TIMESTAMP WITH TIME ZONE,
1707  timestamp_tz2   TIMESTAMP WITH TIME ZONE,
1708
1709  UNIQUE(k1, k2, k3)
1710)
1711EOF
1712
1713    $dbh->disconnect;
1714
1715    # Create test subclass
1716
1717    package MyPgObject;
1718
1719    use Rose::DB::Object::Helpers qw(clone);
1720
1721    our @ISA = qw(Rose::DB::Object);
1722
1723    sub init_db { Rose::DB->new('pg') }
1724
1725    MyPgObject->meta->table('rose_db_object_test');
1726
1727    MyPgObject->meta->columns
1728    (
1729      name     => { type => 'varchar', length => 32, overflow => 'truncate' },
1730      code     => { type => 'char', length => 6, overflow => 'truncate' },
1731      id       => { primary_key => 1, not_null => 1 },
1732      k1       => { type => 'int' },
1733      k2       => { type => 'int', lazy => 1 },
1734      k3       => { type => 'int' },
1735      ($PG_HAS_CHKPASS ? (passwd => { type => 'chkpass', alias => 'password' }) : ()),
1736      flag     => { type => 'boolean', default => 1 },
1737      flag2    => { type => 'boolean' },
1738      status   => { default => 'active', add_methods => [ qw(get set) ] },
1739      start    => { type => 'date', default => '12/24/1980', lazy => 1 },
1740      save     => { type => 'scalar' },
1741      nums     => { type => 'array' },
1742      bitz     => { type => 'bitfield', bits => 5, default => 101, alias => 'bits' },
1743      decs     => { type => 'decimal', precision => 10, scale => 2 },
1744      dur      => { type => 'interval', scale => 6, default => '2 months 5 days 3 seconds' },
1745      epoch    => { type => 'epoch', default => '11/30/1999 9:30pm' },
1746      hiepoch  => { type => 'epoch hires', default => '1144004926.123456' },
1747      bint1    => { type => 'bigint', default => '9223372036854775800' },
1748      bint2    => { type => 'bigint', default => '-9223372036854775800' },
1749      bint3    => { type => 'bigint', with_init => 1, check_in => [ '9223372036854775000', 5 ] },
1750      bint4    => { type => 'bigint' },
1751      tee_time  => { type => 'time' },
1752      tee_time0 => { type => 'time', scale => 0 },
1753      tee_time5 => { type => 'time', scale => 5, default => '12:34:56.123456789' },
1754      tee_time9 => { type => 'time', scale => 9 },
1755      #last_modified => { type => 'timestamp' },
1756      date_created => { type => 'timestamp' },
1757      date_created_tz => { type => 'timestamp with time zone' },
1758      timestamp_tz2 => { type => 'timestamp with time zone', time_zone => 'Antarctica/Vostok' },
1759      main::nonpersistent_column_definitions(),
1760    );
1761
1762    MyPgObject->meta->add_unique_key('save');
1763
1764    MyPgObject->meta->add_unique_key([ qw(k1 k2 k3) ]);
1765
1766    MyPgObject->meta->add_columns(
1767      Rose::DB::Object::Metadata::Column::Timestamp->new(
1768        name => 'last_modified'));
1769
1770    eval { MyPgObject->meta->initialize };
1771    Test::More::ok($@, 'meta->initialize() reserved method');
1772
1773    MyPgObject->meta->alias_column(save => 'save_col');
1774
1775    eval { MyPgObject->meta->initialize };
1776    Test::More::ok($@, 'meta->initialize() no override');
1777
1778    MyPgObject->meta->initialize(preserve_existing => 1);
1779
1780    Test::More::is(MyPgObject->meta->column('id')->is_primary_key_member, 1, 'is_primary_key_member - pg');
1781    Test::More::is(MyPgObject->meta->column('id')->primary_key_position, 1, 'primary_key_position 1 - pg');
1782    Test::More::ok(!defined MyPgObject->meta->column('k1')->primary_key_position, 'primary_key_position 2 - pg');
1783    MyPgObject->meta->column('k1')->primary_key_position(7);
1784    Test::More::ok(!defined MyPgObject->meta->column('k1')->primary_key_position, 'primary_key_position 3 - pg');
1785
1786    sub init_bint3 { '9223372036854775000' }
1787  }
1788
1789  #
1790  # MySQL
1791  #
1792
1793  my $db_version;
1794
1795  eval
1796  {
1797    my $db = Rose::DB->new('mysql_admin');
1798    $dbh = $db->retain_dbh() or die Rose::DB->error;
1799    $db_version = $db->database_version;
1800  };
1801
1802  if(!$@ && $dbh)
1803  {
1804    our $HAVE_MYSQL = 1;
1805
1806    # Drop existing table and create schema, ignoring errors
1807    {
1808      local $dbh->{'RaiseError'} = 0;
1809      local $dbh->{'PrintError'} = 0;
1810      $dbh->do('DROP TABLE rose_db_object_test');
1811      $dbh->do('DROP TABLE rose_db_object_test2');
1812    }
1813
1814    # MySQL 5.0.3 or later has a completely stupid "native" BIT type
1815    my $bit_col1 =
1816      ($db_version >= 5_000_003) ?
1817        q(bitz  BIT(5) NOT NULL DEFAULT B'00101') :
1818        q(bitz  BIT(5) NOT NULL DEFAULT '00101');
1819
1820    my $bit_col2 =
1821      ($db_version >= 5_000_003) ?
1822        q(bitz2  BIT(2) NOT NULL DEFAULT B'00') :
1823        q(bitz2  BIT(2) NOT NULL DEFAULT '0');
1824
1825    my $set_col =
1826      ($db_version >= 5_000_000) ?
1827        q(items  SET('a','b','c') NOT NULL DEFAULT 'a,c') :
1828        q(items  VARCHAR(255) NOT NULL DEFAULT 'a,c');
1829
1830    my $engine = '';
1831
1832    if(our $INNODB = mysql_supports_innodb())
1833    {
1834      $engine = 'ENGINE=InnoDB';
1835    }
1836
1837    $dbh->do(<<"EOF");
1838CREATE TABLE rose_db_object_test
1839(
1840  id             INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
1841  k1             INT,
1842  k2             INT,
1843  k3             INT,
1844  name           VARCHAR(32) NOT NULL,
1845  code           CHAR(6),
1846  flag           TINYINT(1) NOT NULL,
1847  flag2          TINYINT(1),
1848  status         VARCHAR(32) DEFAULT 'active',
1849  $bit_col1,
1850  $bit_col2,
1851  $set_col,
1852  bitz3          BIT(4),
1853  decs           FLOAT(10,2),
1854  nums           VARCHAR(255),
1855  start          DATE,
1856  save           INT,
1857  enums          ENUM('foo', 'bar', 'baz') DEFAULT 'foo',
1858  ndate          DATE NOT NULL DEFAULT '0000-00-00',
1859  dur            VARCHAR(255) DEFAULT '2 months 5 days 3 seconds',
1860  epoch          INT DEFAULT 943997400,
1861  hiepoch        DECIMAL(16,6),
1862  zepoch         INT NOT NULL DEFAULT 0,
1863  tee_time       VARCHAR(32),
1864  tee_time0      VARCHAR(32),
1865  tee_time5      VARCHAR(32) DEFAULT '12:34:56.123456789',
1866  tee_time9      VARCHAR(32),
1867  dt_default     TIMESTAMP,
1868  last_modified  TIMESTAMP,
1869  date_created   TIMESTAMP,
1870
1871  UNIQUE(k1, k2, k3)
1872)
1873$engine
1874EOF
1875
1876    $dbh->do(<<"EOF");
1877CREATE TABLE rose_db_object_test2
1878(
1879  k1             INT NOT NULL,
1880  k2             INT NOT NULL,
1881  name           VARCHAR(32),
1882
1883  UNIQUE(k1, k2)
1884)
1885EOF
1886
1887    $dbh->disconnect;
1888
1889    # Create test subclass
1890
1891    package MyMySQLObject;
1892
1893    use Rose::DB::Object::Helpers qw(clone);
1894
1895    our @ISA = qw(Rose::DB::Object);
1896
1897    sub init_db { Rose::DB->new('mysql') }
1898
1899    MyMySQLObject->meta->allow_inline_column_values(1);
1900
1901    MyMySQLObject->meta->table('rose_db_object_test');
1902
1903    MyMySQLObject->meta->columns
1904    (
1905      name     => { type => 'varchar', length => 32 },
1906      code     => { type => 'char', length => 6 },
1907      id       => { primary_key => 1, not_null => 1 },
1908      k1       => { type => 'int' },
1909      k2       => { type => 'int', lazy => 1 },
1910      k3       => { type => 'int' },
1911      flag     => { type => 'boolean', default => 1 },
1912      flag2    => { type => 'boolean' },
1913      status   => { default => 'active', methods => [ qw(get_set get set) ] },
1914      start    => { type => 'date', default => '12/24/1980', lazy => 1 },
1915      ndate    => { type => 'date', not_null => 1, default => '0000-00-00' },
1916      save     => { type => 'scalar' },
1917      nums     => { type => 'array' },
1918      enums    => { type => 'enum', values => [ qw(foo bar baz) ], default => 'foo' },
1919      bitz     => { type => 'bitfield', bits => 5, default => 101, alias => 'bits' },
1920      bitz2    => { type => 'bits', bits => 2, default => '0' },
1921      bitz3    => { type => 'bits', bits => 4 },
1922      items    => { type => 'set', check_in => [ qw(a b c) ], default => 'a,c' },
1923      decs     => { type => 'decimal', precision => 10, scale => 2 },
1924      dur      => { type => 'interval', scale => 6, default => '2 months 5 days 3 seconds' },
1925      epoch    => { type => 'epoch', default => '11/30/1999 9:30pm' },
1926      hiepoch  => { type => 'epoch hires', default => '1144004926.123456' },
1927      zepoch   => { type => 'epoch', default => 0, not_null => 1, time_zone => 'UTC' },
1928      tee_time  => { type => 'time' },
1929      tee_time0 => { type => 'time', scale => 0 },
1930      tee_time5 => { type => 'time', scale => 5, default => '12:34:56.123456789' },
1931      tee_time9 => { type => 'time', scale => 9 },
1932      dt_default => {  type => 'timestamp', default => 'now()' },
1933      last_modified => { type => 'timestamp' },
1934      date_created  => { type => 'timestamp' },
1935      main::nonpersistent_column_definitions(),
1936    );
1937
1938    eval { MyMySQLObject->meta->initialize };
1939    Test::More::ok($@, 'meta->initialize() reserved method');
1940
1941    MyMySQLObject->meta->alias_column(save => 'save_col');
1942
1943    eval { MyMySQLObject->meta->initialize };
1944    Test::More::ok($@, 'meta->initialize() no override');
1945
1946    MyMySQLObject->meta->add_unique_key('save');
1947    MyMySQLObject->meta->add_unique_key([ qw(k1 k2 k3) ]);
1948
1949    MyMySQLObject->meta->initialize(preserve_existing => 1);
1950
1951    Test::More::is(MyMySQLObject->meta->column('id')->is_primary_key_member, 1, 'is_primary_key_member - mysql');
1952    Test::More::is(MyMySQLObject->meta->column('id')->primary_key_position, 1, 'primary_key_position 1 - mysql');
1953    Test::More::ok(!defined MyMySQLObject->meta->column('k1')->primary_key_position, 'primary_key_position 2 - mysql');
1954    MyMySQLObject->meta->column('k1')->primary_key_position(7);
1955    Test::More::ok(!defined MyMySQLObject->meta->column('k1')->primary_key_position, 'primary_key_position 3 - mysql');
1956
1957    package MyMPKMySQLObject;
1958
1959    use Rose::DB::Object;
1960
1961    our @ISA = qw(Rose::DB::Object);
1962
1963    sub init_db { Rose::DB->new('mysql') }
1964
1965    MyMPKMySQLObject->meta->table('rose_db_object_test2');
1966
1967    MyMPKMySQLObject->meta->columns
1968    (
1969      k1          => { type => 'int', not_null => 1 },
1970      k2          => { type => 'int', not_null => 1 },
1971      name        => { type => 'varchar', length => 32 },
1972    );
1973
1974    MyMPKMySQLObject->meta->primary_key_columns('k1', 'k2');
1975
1976    my $i = 1;
1977
1978    MyMPKMySQLObject->meta->setup
1979    (
1980      primary_key_generator => sub
1981      {
1982        my($meta, $db) = @_;
1983
1984        my $k1 = $i++;
1985        my $k2 = $i++;
1986
1987        return $k1, $k2;
1988      },
1989    );
1990  }
1991
1992  #
1993  # Informix
1994  #
1995
1996  eval
1997  {
1998    $dbh = Rose::DB->new('informix_admin')->retain_dbh()
1999      or die Rose::DB->error;
2000  };
2001
2002  if(!$@ && $dbh)
2003  {
2004    our $HAVE_INFORMIX = 1;
2005
2006    # Drop existing table and create schema, ignoring errors
2007    {
2008      local $dbh->{'RaiseError'} = 0;
2009      local $dbh->{'PrintError'} = 0;
2010      $dbh->do('DROP TABLE rose_db_object_test');
2011    }
2012
2013    $dbh->do(<<"EOF");
2014CREATE TABLE rose_db_object_test
2015(
2016  id             SERIAL NOT NULL PRIMARY KEY,
2017  k1             INT,
2018  k2             INT,
2019  k3             INT,
2020  name           VARCHAR(32) NOT NULL,
2021  code           CHAR(6),
2022  flag           BOOLEAN NOT NULL,
2023  flag2          BOOLEAN,
2024  status         VARCHAR(32) DEFAULT 'active',
2025  bitz           VARCHAR(5) DEFAULT '00101' NOT NULL,
2026  decs           DECIMAL(10,2),
2027  nums           VARCHAR(255),
2028  start          DATE,
2029  save           INT,
2030  names          SET(VARCHAR(64) NOT NULL),
2031  last_modified  DATETIME YEAR TO FRACTION(5),
2032  date_created   DATETIME YEAR TO FRACTION(5)
2033)
2034EOF
2035
2036    $dbh->disconnect;
2037
2038    # Create test subclass
2039
2040    package MyInformixObject;
2041
2042    use Rose::DB::Object::Helpers qw(clone);
2043
2044    our @ISA = qw(Rose::DB::Object);
2045
2046    sub init_db { Rose::DB->new('informix') }
2047
2048    MyInformixObject->meta->allow_inline_column_values(1);
2049
2050    MyInformixObject->meta->table('rose_db_object_test');
2051
2052    MyInformixObject->meta->columns
2053    (
2054      name     => { type => 'varchar', length => 32, overflow => 'truncate' },
2055      code     => { type => 'char', length => 6, overflow => 'truncate' },
2056      id       => { type => 'serial', primary_key => 1, not_null => 1 },
2057      k1       => { type => 'int' },
2058      k2       => { type => 'int', lazy => 1 },
2059      k3       => { type => 'int' },
2060      flag     => { type => 'boolean', default => 1 },
2061      flag2    => { type => 'boolean' },
2062      status   => { default => 'active', add_methods => [ qw(get set) ] },
2063      start    => { type => 'date', default => '12/24/1980', lazy => 1 },
2064      save     => { type => 'scalar' },
2065      nums     => { type => 'array' },
2066      bitz     => { type => 'bitfield', bits => 5, default => 101, alias => 'bits' },
2067      decs     => { type => 'decimal', precision => 10, scale => 2 },
2068      names    => { type => 'set' },
2069      last_modified => { type => 'timestamp' },
2070      date_created  => { type => 'datetime year to fraction(5)' },
2071      main::nonpersistent_column_definitions(),
2072    );
2073
2074    eval { MyInformixObject->meta->initialize };
2075    Test::More::ok($@, 'meta->initialize() reserved method');
2076
2077    MyInformixObject->meta->prepare_options({ix_CursorWithHold => 1});
2078
2079    MyInformixObject->meta->alias_column(save => 'save_col');
2080
2081    eval { MyInformixObject->meta->initialize };
2082    Test::More::ok($@, 'meta->initialize() no override');
2083
2084    MyInformixObject->meta->add_unique_key('save');
2085    MyInformixObject->meta->add_unique_key([ qw(k1 k2 k3) ]);
2086
2087    MyInformixObject->meta->initialize(preserve_existing => 1);
2088
2089    Test::More::is(MyInformixObject->meta->column('id')->is_primary_key_member, 1, 'is_primary_key_member - informix');
2090    Test::More::is(MyInformixObject->meta->column('id')->primary_key_position, 1, 'primary_key_position 1 - informix');
2091    Test::More::ok(!defined MyInformixObject->meta->column('k1')->primary_key_position, 'primary_key_position 2 - informix');
2092    MyInformixObject->meta->column('k1')->primary_key_position(7);
2093    Test::More::ok(!defined MyInformixObject->meta->column('k1')->primary_key_position, 'primary_key_position 3 - informix');
2094  }
2095
2096  #
2097  # SQLite
2098  #
2099
2100  eval
2101  {
2102    $dbh = Rose::DB->new('sqlite_admin')->retain_dbh()
2103      or die Rose::DB->error;
2104  };
2105
2106  if(!$@ && $dbh)
2107  {
2108    our $HAVE_SQLITE = 1;
2109
2110    #
2111    # Method name conflict tests
2112    #
2113
2114    local $@;
2115
2116     eval
2117     {
2118       package MyNameConflictB;
2119
2120       our @ISA = qw(Rose::DB::Object);
2121
2122       sub init_db { Rose::DB->new('sqlite') }
2123
2124       __PACKAGE__->meta->setup
2125       (
2126         table   => 'foob',
2127         columns => [ qw(id blee) ],
2128       );
2129
2130       package MyNameConflictA;
2131
2132       our @ISA = qw(Rose::DB::Object);
2133
2134       sub init_db { Rose::DB->new('sqlite') }
2135
2136       __PACKAGE__->meta->setup
2137       (
2138         table => 'fooa',
2139         columns => [ qw(bar baz) ],
2140         foreign_keys =>
2141         [
2142           new =>
2143           {
2144             class => 'MyNameConflictB',
2145             key_columns => { baz => 'id' },
2146           },
2147         ],
2148       );
2149     };
2150
2151    like($@, qr/Rose::DB::Object defines a method with the same name/, 'method name conflict');
2152
2153    # Drop existing table and create schema, ignoring errors
2154    {
2155      local $dbh->{'RaiseError'} = 0;
2156      local $dbh->{'PrintError'} = 0;
2157      $dbh->do('DROP TABLE rose_db_object_test');
2158      $dbh->do('DROP TABLE rose_db_object_test2');
2159    }
2160
2161    $dbh->do(<<"EOF");
2162CREATE TABLE rose_db_object_test
2163(
2164  id             INTEGER PRIMARY KEY AUTOINCREMENT,
2165  k1             INT,
2166  k2             INT,
2167  k3             INT,
2168  name           VARCHAR(32) NOT NULL,
2169  code           CHAR(6),
2170  flag           BOOLEAN NOT NULL,
2171  flag2          BOOLEAN,
2172  status         VARCHAR(32) DEFAULT 'active',
2173  bitz           VARCHAR(5) DEFAULT '00101' NOT NULL,
2174  decs           DECIMAL(10,2),
2175  start          DATE,
2176  save           INT,
2177  nums           VARCHAR(255),
2178  data           BLOB,
2179  last_modified  TIMESTAMP,
2180  date_created   TIMESTAMP,
2181
2182  UNIQUE(k1, k2, k3)
2183)
2184EOF
2185
2186    $dbh->do(<<"EOF");
2187CREATE TABLE rose_db_object_test2
2188(
2189  k1             INT NOT NULL,
2190  k2             INT NOT NULL,
2191  name           VARCHAR(32),
2192
2193  UNIQUE(k1, k2)
2194)
2195EOF
2196
2197    $dbh->disconnect;
2198
2199    # Create test subclass
2200
2201    package MySQLiteObject;
2202
2203    use Rose::DB::Object::Helpers qw(clone);
2204
2205    our @ISA = qw(Rose::DB::Object);
2206
2207    sub init_db { Rose::DB->new('sqlite') }
2208
2209    MySQLiteObject->meta->table('rose_db_object_test');
2210
2211    MySQLiteObject->meta->columns
2212    (
2213      name     => { type => 'varchar', length => 32, overflow => 'truncate' },
2214      code     => { type => 'char', length => 6, overflow => 'truncate' },
2215      id       => { primary_key => 1, not_null => 1 },
2216      k1       => { type => 'int' },
2217      k2       => { type => 'int', lazy => 1 },
2218      k3       => { type => 'int' },
2219      flag     => { type => 'boolean', default => 1 },
2220      flag2    => { type => 'boolean' },
2221      status   => { default => 'active', add_methods => [ qw(get set) ] },
2222      start    => { type => 'date', default => '12/24/1980', lazy => 1 },
2223      save     => { type => 'scalar' },
2224      nums     => { type => 'array' },
2225      bitz     => { type => 'bitfield', bits => 5, default => 101, alias => 'bits' },
2226      decs     => { type => 'decimal', precision => 10, scale => 2 },
2227      data     => { type => 'blob' },
2228      #last_modified => { type => 'timestamp' },
2229      date_created  => { type => 'scalar' },
2230      main::nonpersistent_column_definitions(),
2231    );
2232
2233    MySQLiteObject->meta->replace_column(date_created => { type => 'timestamp' });
2234
2235    MySQLiteObject->meta->add_unique_key('save');
2236
2237    MySQLiteObject->meta->add_unique_key([ qw(k1 k2 k3) ]);
2238
2239    MySQLiteObject->meta->add_columns(
2240      Rose::DB::Object::Metadata::Column::Timestamp->new(
2241        name => 'last_modified'));
2242
2243    MySQLiteObject->meta->column('id')->add_trigger(inflate => sub { defined $_[1] ? [ $_[1] ] : undef });
2244    MySQLiteObject->meta->column('id')->add_trigger(deflate => sub { ref $_[1] ? (wantarray ? @{$_[1]} : $_[1]->[0]) : $_[1] });
2245
2246    my $pre_inited = 0;
2247    MySQLiteObject->meta->pre_init_hook(sub { $pre_inited++ });
2248
2249    eval { MySQLiteObject->meta->initialize };
2250    Test::More::ok($@, 'meta->initialize() reserved method');
2251    Test::More::is($pre_inited, 1, 'meta->pre_init_hook()');
2252
2253    MySQLiteObject->meta->alias_column(save => 'save_col');
2254
2255    eval { MySQLiteObject->meta->initialize };
2256    Test::More::ok($@, 'meta->initialize() no override');
2257
2258    MySQLiteObject->meta->initialize(preserve_existing => 1);
2259
2260    Test::More::is(MySQLiteObject->meta->column('id')->is_primary_key_member, 1, 'is_primary_key_member - sqlite');
2261    Test::More::is(MySQLiteObject->meta->column('id')->primary_key_position, 1, 'primary_key_position 1 - sqlite');
2262    Test::More::ok(!defined MySQLiteObject->meta->column('k1')->primary_key_position, 'primary_key_position 2 - sqlite');
2263    MySQLiteObject->meta->column('k1')->primary_key_position(7);
2264    Test::More::ok(!defined MySQLiteObject->meta->column('k1')->primary_key_position, 'primary_key_position 3 - sqlite');
2265
2266    package MyMPKSQLiteObject;
2267
2268    use Rose::DB::Object;
2269
2270    our @ISA = qw(Rose::DB::Object);
2271
2272    sub init_db { Rose::DB->new('sqlite') }
2273
2274    MyMPKSQLiteObject->meta->table('rose_db_object_test2');
2275
2276    MyMPKSQLiteObject->meta->columns
2277    (
2278      k1          => { type => 'int', not_null => 1 },
2279      k2          => { type => 'int', not_null => 1 },
2280      name        => { type => 'varchar', length => 32 },
2281    );
2282
2283    MyMPKSQLiteObject->meta->primary_key_columns('k1', 'k2');
2284
2285    MyMPKSQLiteObject->meta->initialize;
2286
2287    my $i = 1;
2288
2289    MyMPKSQLiteObject->meta->primary_key_generator(sub
2290    {
2291      my($meta, $db) = @_;
2292
2293      my $k1 = $i++;
2294      my $k2 = $i++;
2295
2296      return $k1, $k2;
2297    });
2298  }
2299
2300  #
2301  # Oracle
2302  #
2303
2304  eval
2305  {
2306    $dbh = Rose::DB->new('oracle_admin')->retain_dbh()
2307      or die Rose::DB->error;
2308  };
2309
2310  if(!$@ && $dbh)
2311  {
2312    our $HAVE_ORACLE = 1;
2313
2314    # Drop existing table and create schema, ignoring errors
2315    {
2316      local $dbh->{'RaiseError'} = 0;
2317      local $dbh->{'PrintError'} = 0;
2318      $dbh->do('DROP TABLE rose_db_object_test');
2319      $dbh->do('DROP SEQUENCE rose_db_object_test_id_seq');
2320    }
2321
2322    $dbh->do(<<"EOF");
2323CREATE TABLE rose_db_object_test
2324(
2325  id              INT NOT NULL PRIMARY KEY,
2326  k1              INT,
2327  k2              INT,
2328  k3              INT,
2329  name            VARCHAR(32) NOT NULL,
2330  code            CHAR(6),
2331  flag            CHAR(1) NOT NULL CHECK(flag IN ('t', 'f')),
2332  flag2           CHAR(1) CHECK(flag2 IN ('t', 'f')),
2333  status          VARCHAR(32) DEFAULT 'active',
2334  bitz            VARCHAR(5) DEFAULT '00101' NOT NULL,
2335  decs            NUMBER(10,2),
2336  nums            VARCHAR(255),
2337  start_date      DATE,
2338  save            INT,
2339  claim#          INT,
2340  key             INT,
2341  last_modified   TIMESTAMP,
2342  date_created    TIMESTAMP,
2343  date_created_tz TIMESTAMP WITH TIME ZONE,
2344  timestamp_tz2   TIMESTAMP WITH TIME ZONE
2345)
2346EOF
2347
2348    $dbh->do(<<"EOF");
2349CREATE SEQUENCE rose_db_object_test_id_seq
2350EOF
2351
2352    $dbh->do(<<"EOF");
2353CREATE OR REPLACE TRIGGER rose_db_object_test_insert
2354BEFORE INSERT ON rose_db_object_test FOR EACH ROW
2355BEGIN
2356  SELECT NVL(:new.id, rose_db_object_test_id_seq.nextval) INTO :new.id FROM dual;
2357END;
2358EOF
2359
2360    $dbh->commit;
2361    $dbh->disconnect;
2362
2363    # Create test subclass
2364
2365    package MyOracleObject;
2366
2367    use Rose::DB::Object::Helpers qw(clone);
2368
2369    our @ISA = qw(Rose::DB::Object);
2370
2371    sub init_db { Rose::DB->new('oracle') }
2372
2373    MyOracleObject->meta->allow_inline_column_values(1);
2374
2375    MyOracleObject->meta->table('rose_db_object_test');
2376
2377    MyOracleObject->meta->columns
2378    (
2379      name     => { type => 'varchar', length => 32, overflow => 'truncate' },
2380      code     => { type => 'char', length => 6, overflow => 'truncate' },
2381      id       => { type => 'serial', primary_key => 1, not_null => 1 },
2382      k1       => { type => 'int' },
2383      k2       => { type => 'int', lazy => 1 },
2384      k3       => { type => 'int' },
2385      key      => { type => 'int' },
2386      flag     => { type => 'boolean', default => 1 },
2387      flag2    => { type => 'boolean' },
2388      status   => { default => 'active', add_methods => [ qw(get set) ] },
2389      start_date => { type => 'date', default => '12/24/1980', lazy => 1 },
2390      save     => { type => 'scalar' },
2391      'claim#' => { type => 'int' },
2392      nums     => { type => 'array' },
2393      bitz     => { type => 'bitfield', bits => 5, default => 101, alias => 'bits' },
2394      decs     => { type => 'decimal', precision => 10, scale => 2 },
2395      last_modified => { type => 'timestamp' },
2396      date_created  => { type => 'timestamp' },
2397      date_created_tz => { type => 'timestamp with time zone' },
2398      timestamp_tz2 => { type => 'timestamp with time zone', time_zone => 'Antarctica/Vostok' },
2399      main::nonpersistent_column_definitions(),
2400    );
2401
2402    eval { MyOracleObject->meta->initialize };
2403    Test::More::ok($@, 'meta->initialize() reserved method');
2404
2405    MyOracleObject->meta->prepare_options({ix_CursorWithHold => 1});
2406
2407    MyOracleObject->meta->alias_column(save => 'save_col');
2408
2409    eval { MyOracleObject->meta->initialize };
2410    Test::More::ok($@, 'meta->initialize() no override');
2411
2412    MyOracleObject->meta->add_unique_key('save');
2413    MyOracleObject->meta->add_unique_key('key');
2414    MyOracleObject->meta->add_unique_key([ qw(k1 k2 k3) ]);
2415
2416    MyOracleObject->meta->initialize(preserve_existing => 1);
2417
2418    Test::More::is(MyOracleObject->meta->column('id')->is_primary_key_member, 1, 'is_primary_key_member - oracle');
2419    Test::More::is(MyOracleObject->meta->column('id')->primary_key_position, 1, 'primary_key_position 1 - oracle');
2420    Test::More::ok(!defined MyOracleObject->meta->column('k1')->primary_key_position, 'primary_key_position 2 - oracle');
2421    MyOracleObject->meta->column('k1')->primary_key_position(7);
2422    Test::More::ok(!defined MyOracleObject->meta->column('k1')->primary_key_position, 'primary_key_position 3 - oracle');
2423  }
2424}
2425
2426END
2427{
2428  # Delete test table
2429
2430  if($HAVE_PG)
2431  {
2432    # PostgreSQL
2433    my $dbh = Rose::DB->new('pg_admin')->retain_dbh()
2434      or die Rose::DB->error;
2435
2436    $dbh->do('DROP TABLE rose_db_object_test');
2437    $dbh->do('DROP TABLE rose_db_object_private.rose_db_object_test');
2438    $dbh->do('DROP SCHEMA rose_db_object_private CASCADE');
2439
2440    $dbh->disconnect;
2441  }
2442
2443  if($HAVE_MYSQL)
2444  {
2445    my $dbh = Rose::DB->new('mysql_admin')->retain_dbh()
2446      or die Rose::DB->error;
2447
2448    $dbh->do('DROP TABLE rose_db_object_test');
2449    $dbh->do('DROP TABLE rose_db_object_test2');
2450
2451    $dbh->disconnect;
2452  }
2453
2454  if($HAVE_INFORMIX)
2455  {
2456    my $dbh = Rose::DB->new('informix_admin')->retain_dbh()
2457      or die Rose::DB->error;
2458
2459    $dbh->do('DROP TABLE rose_db_object_test');
2460
2461    $dbh->disconnect;
2462  }
2463
2464  if($HAVE_SQLITE)
2465  {
2466    my $dbh = Rose::DB->new('sqlite_admin')->retain_dbh()
2467      or die Rose::DB->error;
2468
2469    $dbh->do('DROP TABLE rose_db_object_test');
2470    $dbh->do('DROP TABLE rose_db_object_test2');
2471
2472    $dbh->disconnect;
2473  }
2474
2475  if($HAVE_ORACLE)
2476  {
2477    my $dbh = Rose::DB->new('oracle_admin')->retain_dbh()
2478      or die Rose::DB->error;
2479
2480    $dbh->do('DROP TABLE rose_db_object_test');
2481    $dbh->do('DROP SEQUENCE rose_db_object_test_id_seq');
2482
2483    $dbh->disconnect;
2484  }
2485}
2486