Some cleanups and code dedup of Top and FetchFirst limit dialects
[dbsrgits/DBIx-Class.git] / t / 98savepoints.t
1 use strict;
2 use warnings;
3
4 use Test::More;
5 use DBIx::Class::Optional::Dependencies ();
6
7 my $env2optdep = {
8   DBICTEST_PG => 'rdbms_pg',
9   DBICTEST_MYSQL => 'test_rdbms_mysql',
10 };
11
12 plan skip_all => join (' ',
13   'Set $ENV{DBICTEST_PG_DSN} and/or $ENV{DBICTEST_MYSQL_DSN} _USER and _PASS to run these tests.',
14 ) unless grep { $ENV{"${_}_DSN"} } keys %$env2optdep;
15
16 use lib qw(t/lib);
17 use DBICTest;
18 use DBICTest::Stats;
19
20 my $schema;
21
22 for my $prefix (keys %$env2optdep) { SKIP: {
23   my ($dsn, $user, $pass) = map { $ENV{"${prefix}_$_"} } qw/DSN USER PASS/;
24
25   skip ("Skipping tests with $prefix: set \$ENV{${prefix}_DSN} _USER and _PASS", 1)
26     unless $dsn;
27
28   skip ("Testing with ${prefix}_DSN needs " . DBIx::Class::Optional::Dependencies->req_missing_for( $env2optdep->{$prefix} ), 1)
29     unless  DBIx::Class::Optional::Dependencies->req_ok_for($env2optdep->{$prefix});
30
31   $schema = DBICTest::Schema->connect ($dsn,$user,$pass,{ auto_savepoint => 1 });
32
33   my $create_sql;
34   $schema->storage->ensure_connected;
35   if ($schema->storage->isa('DBIx::Class::Storage::DBI::Pg')) {
36     $create_sql = "CREATE TABLE artist (artistid serial PRIMARY KEY, name VARCHAR(100), rank INTEGER NOT NULL DEFAULT '13', charfield CHAR(10))";
37     $schema->storage->dbh->do('SET client_min_messages=WARNING');
38   }
39   elsif ($schema->storage->isa('DBIx::Class::Storage::DBI::mysql')) {
40     $create_sql = "CREATE TABLE artist (artistid INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100), rank INTEGER NOT NULL DEFAULT '13', charfield CHAR(10)) ENGINE=InnoDB";
41   }
42   else {
43     skip( 'Untested driver ' . $schema->storage, 1 );
44   }
45
46   note "Testing $prefix";
47
48   my $stats = DBICTest::Stats->new;
49   $schema->storage->debugobj($stats);
50   $schema->storage->debug(1);
51
52   $schema->storage->dbh->do ('DROP TABLE IF EXISTS artist');
53   $schema->storage->dbh->do ($create_sql);
54
55   $schema->resultset('Artist')->create({ name => 'foo' });
56
57   $schema->txn_begin;
58
59   my $arty = $schema->resultset('Artist')->find(1);
60
61   my $name = $arty->name;
62
63   # First off, test a generated savepoint name
64   $schema->svp_begin;
65
66   cmp_ok($stats->{'SVP_BEGIN'}, '==', 1, 'Statistics svp_begin tickled');
67
68   $arty->update({ name => 'Jheephizzy' });
69
70   $arty->discard_changes;
71
72   cmp_ok($arty->name, 'eq', 'Jheephizzy', 'Name changed');
73
74   # Rollback the generated name
75   # Active: 0
76   $schema->svp_rollback;
77
78   cmp_ok($stats->{'SVP_ROLLBACK'}, '==', 1, 'Statistics svp_rollback tickled');
79
80   $arty->discard_changes;
81
82   cmp_ok($arty->name, 'eq', $name, 'Name rolled back');
83
84   $arty->update({ name => 'Jheephizzy'});
85
86   # Active: 0 1
87   $schema->svp_begin('testing1');
88
89   $arty->update({ name => 'yourmom' });
90
91   # Active: 0 1 2
92   $schema->svp_begin('testing2');
93
94   $arty->update({ name => 'gphat' });
95   $arty->discard_changes;
96   cmp_ok($arty->name, 'eq', 'gphat', 'name changed');
97   # Active: 0 1 2
98   # Rollback doesn't DESTROY the savepoint, it just rolls back to the value
99   # at it's conception
100   $schema->svp_rollback('testing2');
101   $arty->discard_changes;
102   cmp_ok($arty->name, 'eq', 'yourmom', 'testing2 reverted');
103
104   # Active: 0 1 2 3
105   $schema->svp_begin('testing3');
106   $arty->update({ name => 'coryg' });
107   # Active: 0 1 2 3 4
108   $schema->svp_begin('testing4');
109   $arty->update({ name => 'watson' });
110
111   # Release 3, which implicitly releases 4
112   # Active: 0 1 2
113   $schema->svp_release('testing3');
114   $arty->discard_changes;
115   cmp_ok($arty->name, 'eq', 'watson', 'release left data');
116   # This rolls back savepoint 2
117   # Active: 0 1 2
118   $schema->svp_rollback;
119   $arty->discard_changes;
120   cmp_ok($arty->name, 'eq', 'yourmom', 'rolled back to 2');
121
122   # Rollback the original savepoint, taking us back to the beginning, implicitly
123   # rolling back savepoint 1 and 2
124   $schema->svp_rollback('savepoint_0');
125   $arty->discard_changes;
126   cmp_ok($arty->name, 'eq', 'foo', 'rolled back to start');
127
128   $schema->txn_commit;
129
130   # And now to see if txn_do will behave correctly
131   $schema->txn_do (sub {
132     $schema->txn_do (sub {
133       $arty->name ('Muff');
134       $arty->update;
135     });
136
137     eval {
138       $schema->txn_do (sub {
139         $arty->name ('Moff');
140         $arty->update;
141         $arty->discard_changes;
142         is($arty->name,'Moff','Value updated in nested transaction');
143         $schema->storage->dbh->do ("GUARANTEED TO PHAIL");
144       });
145     };
146
147     ok ($@,'Nested transaction failed (good)');
148
149     $arty->discard_changes;
150
151     is($arty->name,'Muff','auto_savepoint rollback worked');
152
153     $arty->name ('Miff');
154
155     $arty->update;
156   });
157
158   $arty->discard_changes;
159
160   is($arty->name,'Miff','auto_savepoint worked');
161
162   cmp_ok($stats->{'SVP_BEGIN'},'==',7,'Correct number of savepoints created');
163
164   cmp_ok($stats->{'SVP_RELEASE'},'==',3,'Correct number of savepoints released');
165
166   cmp_ok($stats->{'SVP_ROLLBACK'},'==',5,'Correct number of savepoint rollbacks');
167
168   $schema->storage->dbh->do ("DROP TABLE artist");
169 }}
170
171 done_testing;
172
173 END { eval { $schema->storage->dbh->do ("DROP TABLE artist") } if defined $schema }
174