1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
use strict;
use warnings;
use lib "t/lib";
use SQLiteTest;
use Test::More;
use if -d ".git", "Test::FailWarnings";
use DBD::SQLite;
# hooks : just count the commits / rollbacks / updates
my ($n_commits, $n_rollbacks, $n_updates, @update_args);
sub commit_hook { $n_commits += 1; return 0; }
sub rollback_hook { $n_rollbacks += 1; return 0; }
sub update_hook { $n_updates += 1;
@update_args = @_; }
my $sql_count_rows = "SELECT COUNT(foo) FROM hook_test";
foreach my $call_func (@CALL_FUNCS) {
# connect
my $dbh = connect_ok( RaiseError => 1 );
$dbh->do( 'CREATE TEMP TABLE hook_test ( foo )' );
# register the hooks
my $previous_commit_hook = $dbh->$call_func(\&commit_hook,
"commit_hook");
my $previous_rollback_hook = $dbh->$call_func(\&rollback_hook,
"rollback_hook");
my $previous_update_hook = $dbh->$call_func(\&update_hook,
"update_hook");
ok(!$previous_commit_hook, "initial commit hook was undef");
ok(!$previous_rollback_hook, "initial rollback hook was undef");
ok(!$previous_update_hook, "initial update hook was undef");
# a couple of transactions
do_transaction($dbh) for 1..3;
# commit hook should have been called three times
is($n_commits, 3, "3 commits");
# update hook should have been called 30 times
is($n_updates, 30, "30 updates");
# check args transmitted to update hook;
is($update_args[0], DBD::SQLite::INSERT, 'update hook arg 0: INSERT');
is($update_args[1], 'temp', 'update hook arg 1: database');
is($update_args[2], 'hook_test', 'update hook arg 2: table');
ok($update_args[3], 'update hook arg 3: rowid');
# unregister the commit and update hooks, check if previous hooks are returned
$previous_commit_hook = $dbh->$call_func(undef, "commit_hook");
ok($previous_commit_hook eq \&commit_hook,
"previous commit hook correctly returned");
$previous_update_hook = $dbh->$call_func(undef, "update_hook");
ok($previous_update_hook eq \&update_hook,
"previous update hook correctly returned");
# some more transactions .. commit and update hook should not be called
$n_commits = 0;
$n_updates = 0;
do_transaction($dbh) for 1..3;
is($n_commits, 0, "commit hook unregistered");
is($n_updates, 0, "update hook unregistered");
# remember how many rows we had so far
my ($n_rows) = $dbh->selectrow_array($sql_count_rows);
# a commit hook that rejects the transaction
$dbh->$call_func(sub {return 1}, "commit_hook");
allow_warnings { eval {do_transaction($dbh)} }; # in eval() because of RaiseError
ok ($@, "transaction was rejected: $@" );
# no explicit rollback, because SQLite already did it
# eval {$dbh->rollback;};
# ok (!$@, "rollback OK $@");
# rollback hook should have been called
is($n_rollbacks, 1, "1 rollback");
# unregister the rollback hook, check if previous hook is returned
$previous_rollback_hook = $dbh->$call_func(undef, "rollback_hook");
ok($previous_rollback_hook eq \&rollback_hook,
"previous hook correctly returned");
# try transaction again .. rollback hook should not be called
$n_rollbacks = 0;
allow_warnings { eval {do_transaction($dbh)} };
is($n_rollbacks, 0, "rollback hook unregistered");
# check that the rollbacks did really occur
my ($n_rows_after) = $dbh->selectrow_array($sql_count_rows);
is($n_rows, $n_rows_after, "no rows added" );
# unregister commit hook, register an authorizer that forbids delete ops
$dbh->$call_func(undef, "commit_hook");
my @authorizer_args;
my $authorizer = sub {
@authorizer_args = @_;
my $action_code = shift;
my $retval = $action_code == DBD::SQLite::DELETE ? DBD::SQLite::DENY
: DBD::SQLite::OK;
return $retval;
};
$dbh->$call_func($authorizer, "set_authorizer");
# try an insert (should be authorized) and check authorizer args
$dbh->do("INSERT INTO hook_test VALUES ('auth_test')");
is_deeply(\@authorizer_args,
[DBD::SQLite::INSERT, 'hook_test', undef, 'temp', undef],
"args to authorizer (INSERT)");
# try a delete (should be unauthorized)
allow_warnings { eval {$dbh->do("DELETE FROM hook_test WHERE foo = 'auth_test'")} };
ok($@, "delete was rejected with message $@");
is_deeply(\@authorizer_args,
[DBD::SQLite::DELETE, 'hook_test', undef, 'temp', undef],
"args to authorizer (DELETE)");
# unregister the authorizer ... now DELETE should be authorized
$dbh->$call_func(undef, "set_authorizer");
allow_warnings { eval {$dbh->do("DELETE FROM hook_test WHERE foo = 'auth_test'")} };
ok(!$@, "delete was accepted");
}
sub do_transaction {
my $dbh = shift;
$dbh->begin_work;
for my $count (1 .. 10) {
my $rand = rand;
$dbh->do( "INSERT INTO hook_test(foo) VALUES ( $rand )" );
}
$dbh->commit;
}
done_testing;
|