use strict; use warnings; use DBI; use Test::More; use vars qw($test_dsn $test_user $test_password); use lib 't', '.'; require 'lib.pl'; use constant SHOW_PROGRESS => ($ENV{SHOW_PROGRESS} ? 1 : 0); my $COUNT_CONNECT = 4000; # Number of connect/disconnect iterations my $COUNT_PREPARE = 30000; # Number of prepare/execute/finish iterations my $COUNT_BIND = 10000; # Number of bind_param iterations my $have_storable; BEGIN { if (!$ENV{EXTENDED_TESTING}) { plan skip_all => "Skip \$ENV{EXTENDED_TESTING} is not set\n"; } if (not eval { require Proc::ProcessTable }) { plan skip_all => "module Proc::ProcessTable not installed \n"; } $have_storable = eval { require Storable } ? 1 : 0; } my $have_pt_size = grep { $_ eq 'size' } Proc::ProcessTable->new('cache_ttys' => $have_storable)->fields; if (!$have_pt_size) { plan skip_all => "module Proc::ProcessTable does not support size attribute on current platform\n"; } plan skip_all => 'this test is not supported on OpenBSD platform' if $^O eq 'openbsd'; my ($dbh, $sth); $dbh = DbiTestConnect($test_dsn, $test_user, $test_password, { RaiseError => 1, PrintError => 0, AutoCommit => 0 }); $dbh->disconnect; plan tests => 30 * 2; if (not SHOW_PROGRESS and $ENV{TEST_VERBOSE}) { note "You can set \$ENV{SHOW_PROGRESS} to monitor the progress of slow tests"; } sub size { my($p, $pt); $pt = Proc::ProcessTable->new('cache_ttys' => $have_storable); for $p (@{$pt->table()}) { if ($p->pid() == $$) { return $p->size(); } } die "Cannot find my own process?!?\n"; exit 0; } for my $mariadb_server_prepare (0, 1) { note "Testing memory leaks with mariadb_server_prepare=$mariadb_server_prepare\n"; $dbh = DBI->connect($test_dsn, $test_user, $test_password, { RaiseError => 1, PrintError => 0, AutoCommit => 0, mariadb_server_prepare => $mariadb_server_prepare, mariadb_server_prepare_disable_fallback => 1 }); ok $dbh->do("DROP TABLE IF EXISTS dbd_mysql_t60leaks"); my $create= <do($create); my ($size, $prev_size, $ok, $not_ok, $dbh2, $msg); note "Testing memory leaks in connect/disconnect\n"; $msg = "Possible memory leak in connect/disconnect detected"; $ok = 0; $not_ok = 0; $prev_size= undef; for (my $i = 0; $i < $COUNT_CONNECT; $i++) { $dbh2 = DBI->connect($test_dsn, $test_user, $test_password, { RaiseError => 1, PrintError => 0, AutoCommit => 0, mariadb_server_prepare => $mariadb_server_prepare, }); if ($i % 100 == 99) { $size = size(); if (defined($prev_size)) { if ($size == $prev_size) { $ok++; } else { $not_ok++; } } else { $prev_size = $size; $size = size(); } $prev_size = $size; } if (SHOW_PROGRESS) { note sprintf "Progress: %5d/%5d", $i+1, $COUNT_CONNECT; } } $dbh2->disconnect; ok $ok, "\$ok $ok"; ok !$not_ok, "\$not_ok $not_ok"; cmp_ok $ok, '>', $not_ok, "\$ok $ok \$not_ok $not_ok"; note "Testing memory leaks in auto_reconnect\n"; $msg = "Possible memory leak in auto_reconnect"; $ok = 0; $not_ok = 0; undef $prev_size; $dbh2 = DBI->connect($test_dsn, $test_user, $test_password, { RaiseError => 1, PrintError => 0, mariadb_server_prepare => $mariadb_server_prepare, mariadb_auto_reconnect => 1, }); for (my $i = 0; $i < $COUNT_CONNECT; $i++) { $dbh2->disconnect; $dbh2->do("SELECT 1"); if ($i % 100 == 99) { $size = size(); if (defined($prev_size)) { if ($size == $prev_size) { $ok++; } else { $not_ok++; } } else { $prev_size = $size; $size = size(); } $prev_size = $size; } if (SHOW_PROGRESS) { note sprintf "Progress: %5d/%5d", $i+1, $COUNT_CONNECT; } } $dbh2->disconnect; ok $ok; ok !$not_ok, "\$ok $ok \$not_ok $not_ok"; cmp_ok $ok, '>', $not_ok, "\$ok $ok \$not_ok $not_ok"; note "Testing memory leaks in prepare/execute/finish\n"; $msg = "Possible memory leak in prepare/execute/finish detected"; $ok = 0; $not_ok = 0; undef $prev_size; for (my $i = 0; $i < $COUNT_PREPARE; $i++) { my $sth = $dbh->prepare("SELECT * FROM dbd_mysql_t60leaks"); $sth->execute(); $sth->finish(); if ($i % 100 == 99) { $size = size(); if (defined($prev_size)) { if ($size == $prev_size) { $ok++; } else { $not_ok++; } } else { $prev_size = $size; $size = size(); } $prev_size = $size; } if (SHOW_PROGRESS and $i % 10 == 9) { note sprintf "Progress: %5d/%5d", $i+1, $COUNT_PREPARE; } } ok $ok; ok !$not_ok, "\$ok $ok \$not_ok $not_ok"; cmp_ok $ok, '>', $not_ok, "\$ok $ok \$not_ok $not_ok"; note "Testing memory leaks in execute/finish\n"; $msg = "Possible memory leak in execute/finish detected"; $ok = 0; $not_ok = 0; undef $prev_size; { my $sth = $dbh->prepare("SELECT * FROM dbd_mysql_t60leaks"); for (my $i = 0; $i < $COUNT_PREPARE; $i++) { $sth->execute(); $sth->finish(); if ($i % 100 == 99) { $size = size(); if (defined($prev_size)) { if ($size == $prev_size) { $ok++; } else { $not_ok++; } } else { $prev_size = $size; $size = size(); } $prev_size = $size; } if (SHOW_PROGRESS and $i % 10 == 9) { note sprintf "Progress: %5d/%5d", $i+1, $COUNT_PREPARE; } } } ok $ok; ok !$not_ok, "\$ok $ok \$not_ok $not_ok"; cmp_ok $ok, '>', $not_ok, "\$ok $ok \$not_ok $not_ok"; note "Testing memory leaks in bind_param\n"; $msg = "Possible memory leak in bind_param detected"; $ok = 0; $not_ok = 0; undef $prev_size; { my $sth = $dbh->prepare("SELECT * FROM dbd_mysql_t60leaks WHERE id = ? AND name = ?"); for (my $i = 0; $i < $COUNT_BIND; $i++) { $sth->bind_param(1, 0); my $val = "x" x 1000000; $sth->bind_param(2, $val); if ($i % 100 == 99) { $size = size(); if (defined($prev_size)) { if ($size == $prev_size) { $ok++; } else { $not_ok++; } } else { $prev_size = $size; $size = size(); } $prev_size = $size; } if (SHOW_PROGRESS and $i % 10 == 9) { note sprintf "Progress: %5d/%5d", $i+1, $COUNT_BIND; } } } ok $ok; ok !$not_ok, "\$ok $ok \$not_ok $not_ok"; cmp_ok $ok, '>', $not_ok, "\$ok $ok \$not_ok $not_ok"; note "Testing memory leaks in fetchrow_arrayref\n"; $msg= "Possible memory leak in fetchrow_arrayref detected"; $sth= $dbh->prepare("INSERT INTO dbd_mysql_t60leaks VALUES (?, ?)") ; my $dataref= [[1, 'Jochen Wiedmann'], [2, 'Andreas Konig'], [3, 'Tim Bunce'], [4, 'Alligator Descartes'], [5, 'Jonathan Leffler']]; for (@$dataref) { ok $sth->execute($_->[0], $_->[1]), "insert into dbd_mysql_t60leaks values ($_->[0], '$_->[1]')"; } $ok = 0; $not_ok = 0; undef $prev_size; for (my $i = 0; $i < $COUNT_PREPARE; $i++) { { my $sth = $dbh->prepare("SELECT * FROM dbd_mysql_t60leaks"); $sth->execute(); my $row; while ($row = $sth->fetchrow_arrayref()) { } } if ($i % 100 == 99) { $size = size(); if (defined($prev_size)) { if ($size == $prev_size) { ++$ok; } else { ++$not_ok; } } else { $prev_size = $size; $size = size(); } $prev_size = $size; } if (SHOW_PROGRESS and $i % 10 == 9) { note sprintf "Progress: %5d/%5d", $i+1, $COUNT_PREPARE; } } ok $ok; ok !$not_ok, "\$ok $ok \$not_ok $not_ok"; cmp_ok $ok, '>', $not_ok, "\$ok $ok \$not_ok $not_ok"; note "Testing memory leaks in fetchrow_hashref\n"; $msg = "Possible memory leak in fetchrow_hashref detected"; $ok = 0; $not_ok = 0; undef $prev_size; for (my $i = 0; $i < $COUNT_PREPARE; $i++) { { my $sth = $dbh->prepare("SELECT * FROM dbd_mysql_t60leaks"); $sth->execute(); my $row; while ($row = $sth->fetchrow_hashref()) { } } if ($i % 100 == 99) { $size = size(); if (defined($prev_size)) { if ($size == $prev_size) { ++$ok; } else { ++$not_ok; } } else { $prev_size = $size; $size = size(); } $prev_size = $size; } if (SHOW_PROGRESS and $i % 10 == 9) { note sprintf "Progress: %5d/%5d", $i+1, $COUNT_PREPARE; } } ok $ok; ok !$not_ok, "\$ok $ok \$not_ok $not_ok"; cmp_ok $ok, '>', $not_ok, "\$ok $ok \$not_ok $not_ok"; ok $dbh->do("DROP TABLE dbd_mysql_t60leaks"); ok $dbh->disconnect; }