Author: turnstep
Date: Thu Nov 30 20:33:49 2006
New Revision: 8337
Modified:
DBD-Pg/trunk/Changes
DBD-Pg/trunk/Pg.pm
DBD-Pg/trunk/t/03dbmethod.t
Log:
Add statistics_info method, from Brandon Black
Modified: DBD-Pg/trunk/Changes
==============================================================================
--- DBD-Pg/trunk/Changes (original)
+++ DBD-Pg/trunk/Changes Thu Nov 30 20:33:49 2006
@@ -5,6 +5,7 @@
- Fix pg_description join in table_info(). [Max Cohan [EMAIL PROTECTED]
- Fix memory leak in bytea quoting. (CPAN bug #21392). Fix memory leak
in pg_notifies. [Stephen Marshall [EMAIL PROTECTED]
+ - Added statistics_info function [Brandon Black [EMAIL PROTECTED]
1.49 May 7, 2006
- Thanks to Backcountry.com for sponsoring work on this release. [GSM]
Modified: DBD-Pg/trunk/Pg.pm
==============================================================================
--- DBD-Pg/trunk/Pg.pm (original)
+++ DBD-Pg/trunk/Pg.pm Thu Nov 30 20:33:49 2006
@@ -496,6 +496,151 @@
return $sth;
}
+ sub statistics_info {
+ my $dbh = shift;
+ my ($catalog, $schema, $table, $unique_only, $quick, $attr) =
@_;
+
+ ## Catalog is ignored, but table is mandatory
+ return undef unless defined $table and length $table;
+
+ my $version = $dbh->{private_dbdpg}{version};
+
+ # These defaults are for schema-less pre-7.3 versions...
+ my $schema_out = 'NULL::text AS nspname';
+ my $schema_from = '';
+ my $schema_where = '';
+ my @exe_args = ($table);
+
+ my $gotschema = $version >= 70300 ? 1 : 0;
+ my $input_schema = (defined $schema and length $schema) ? 1 : 0;
+
+ if($gotschema) {
+ $schema_out = 'n.nspname';
+ $schema_from = ", ${DBD::Pg::dr::CATALOG}pg_namespace
n";
+ if($input_schema) {
+ $schema_where = 'AND n.nspname = ? AND n.oid =
d.relnamespace';
+ push(@exe_args, $schema);
+ }
+ else {
+ $schema_where = 'AND n.oid = d.relnamespace';
+ }
+ }
+
+ my $table_stats_sql = qq{
+ SELECT d.relpages, d.reltuples, $schema_out
+ FROM pg_class d $schema_from
+ WHERE d.relname = ? $schema_where
+ };
+
+ my $colnames_sql = qq{
+ SELECT
+ a.attnum, a.attname
+ FROM
+ ${DBD::Pg::dr::CATALOG}pg_attribute a,
${DBD::Pg::dr::CATALOG}pg_class d
+ $schema_from
+ WHERE
+ a.attrelid = d.oid AND d.relname = ?
$schema_where
+ };
+
+ my $stats_sql = qq{
+ SELECT
+ c.relname, i.indkey, i.indisunique,
i.indisclustered, a.amname,
+ $schema_out, c.relpages, c.reltuples,
i.indexprs,
+ pg_get_expr(i.indpred,i.indrelid) as predicate
+ FROM
+ ${DBD::Pg::dr::CATALOG}pg_index i,
${DBD::Pg::dr::CATALOG}pg_class c,
+ ${DBD::Pg::dr::CATALOG}pg_class d,
${DBD::Pg::dr::CATALOG}pg_am a
+ $schema_from
+ WHERE
+ d.relname = ? $schema_where AND d.oid =
i.indrelid
+ AND i.indexrelid = c.oid AND c.relam = a.oid
+ ORDER BY
+ i.indisunique desc, a.amname, c.relname
+ };
+
+ my @output_rows;
+
+ # Table-level stats
+ if(!$unique_only) {
+ my $table_stats_sth = $dbh->prepare($table_stats_sql);
+ $table_stats_sth->execute(@exe_args) or return undef;
+ my $tst = $table_stats_sth->fetchrow_hashref or return
undef;
+ push(@output_rows, [
+ undef, # TABLE_CAT
+ $tst->{nspname}, # TABLE_SCHEM
+ $table, # TABLE_NAME
+ undef, # NON_UNIQUE
+ undef, # INDEX_QUALIFIER
+ undef, # INDEX_NAME
+ 'table', # TYPE
+ undef, # ORDINAL_POSITION
+ undef, # COLUMN_NAME
+ undef, # ASC_OR_DESC
+ $tst->{reltuples},# CARDINALITY
+ $tst->{relpages}, # PAGES
+ undef, # FILTER_CONDITION
+ ]);
+ }
+
+ # Fetch the column names for later use
+ my $colnames_sth = $dbh->prepare($colnames_sql);
+ $colnames_sth->execute(@exe_args) or return undef;
+ my $colnames = $colnames_sth->fetchall_hashref('attnum');
+
+ # Fetch the index definitions
+ my $sth = $dbh->prepare($stats_sql);
+ $sth->execute(@exe_args) or return undef;
+
+ STAT_ROW:
+ while(my $row = $sth->fetchrow_hashref) {
+ next if $row->{indexprs}; # We can't return these
accurately via this interface ...
+ next if $unique_only && !$row->{indisunique};
+
+ my $indtype = $row->{indisclustered}
+ ? 'clustered'
+ : ( $row->{amname} eq 'btree' )
+ ? 'btree'
+ : ($row->{amname} eq 'hash' )
+ ? 'hashed' : 'other';
+
+ my $nonunique = $row->{indisunique} ? 0 : 1;
+
+ my @index_row = (
+ undef, # TABLE_CAT
+ $row->{nspname}, # TABLE_SCHEM
+ $table, # TABLE_NAME
+ $nonunique, # NON_UNIQUE
+ undef, # INDEX_QUALIFIER
+ $row->{relname}, # INDEX_NAME
+ $indtype, # TYPE
+ undef, # ORDINAL_POSITION
+ undef, # COLUMN_NAME
+ 'A', # ASC_OR_DESC
+ $row->{reltuples}, # CARDINALITY
+ $row->{relpages}, # PAGES
+ $row->{predicate}, # FILTER_CONDITION
+ );
+
+ my $col_nums = $row->{indkey};
+ $col_nums =~ s/^\s+//;
+ my @col_nums = split(/\s+/, $col_nums);
+
+ my $ord_pos = 1;
+ foreach my $col_num (@col_nums) {
+ my @copy = @index_row;
+ $copy[7] = $ord_pos++; # ORDINAL_POSITION
+ $copy[8] = $colnames->{$col_num}->{attname}; #
COLUMN_NAME
+ push(@output_rows, [EMAIL PROTECTED]);
+ }
+ }
+
+ my @output_colnames = qw/ TABLE_CAT TABLE_SCHEM TABLE_NAME
NON_UNIQUE INDEX_QUALIFIER
+ INDEX_NAME TYPE ORDINAL_POSITION
COLUMN_NAME ASC_OR_DESC
+ CARDINALITY PAGES FILTER_CONDITION /;
+
+ return _prepare_from_data('statistics_info', [EMAIL PROTECTED],
[EMAIL PROTECTED]);
+ }
+
sub primary_key_info {
my $dbh = shift;
Modified: DBD-Pg/trunk/t/03dbmethod.t
==============================================================================
--- DBD-Pg/trunk/t/03dbmethod.t (original)
+++ DBD-Pg/trunk/t/03dbmethod.t Thu Nov 30 20:33:49 2006
@@ -18,7 +18,7 @@
$|=1;
if (defined $ENV{DBI_DSN}) {
- plan tests => 186;
+ plan tests => 196;
}
else {
plan skip_all => 'Cannot run test unless DBI_DSN is defined. See the
README file';
@@ -430,6 +430,131 @@
is_deeply( [EMAIL PROTECTED], $expected, 'DB handle method "primary_key"
returns empty list for invalid table');
#
+# Test of the "statistics_info" database handle method
+#
+
+$sth = $dbh->statistics_info(undef,undef,undef,undef,undef);
+is ($sth, undef, 'DB handle method "statistics_info" returns undef: no table');
+
+# Drop any tables that may exist
+my $fktables = join "," => map { "'dbd_pg_test$_'" } (1..3);
+$SQL = "SELECT relname FROM pg_catalog.pg_class WHERE relkind='r' AND relname
IN ($fktables)";
+{
+ local $SIG{__WARN__} = sub {};
+ for (@{$dbh->selectall_arrayref($SQL)}) {
+ $dbh->do("DROP TABLE $_->[0] CASCADE");
+ }
+}
+
+## Invalid table
+$sth = $dbh->statistics_info(undef,undef,'dbd_pg_test9',undef,undef);
+is ($sth, undef, 'DB handle method "statistics_info" returns undef: bad
table');
+
+## Create some tables with various indexes
+{
+ local $SIG{__WARN__} = sub {};
+ $dbh->do("CREATE TABLE dbd_pg_test1 (a INT, b INT NOT NULL, c INT NOT
NULL, ".
+ "CONSTRAINT dbd_pg_test1_pk PRIMARY KEY (a))");
+ $dbh->do("ALTER TABLE dbd_pg_test1 ADD CONSTRAINT dbd_pg_test1_uc1
UNIQUE (b)");
+ $dbh->do("CREATE UNIQUE INDEX dbd_pg_test1_index_c ON dbd_pg_test1(c)");
+ $dbh->do("CREATE TABLE dbd_pg_test2 (a INT, b INT, c INT, PRIMARY
KEY(a,b), UNIQUE(b,c))");
+ $dbh->do("CREATE INDEX dbd_pg_test2_skipme ON dbd_pg_test2(c,(a+b))");
+ $dbh->do("CREATE TABLE dbd_pg_test3 (a INT, b INT, c INT, PRIMARY
KEY(a)) WITH OIDS");
+ $dbh->do("CREATE UNIQUE INDEX dbd_pg_test3_index_b ON dbd_pg_test3(b)");
+ $dbh->do("CREATE INDEX dbd_pg_test3_index_c ON dbd_pg_test3 USING
hash(c)");
+ $dbh->do("CREATE INDEX dbd_pg_test3_oid ON dbd_pg_test3(oid)");
+ $dbh->do("CREATE UNIQUE INDEX dbd_pg_test3_pred ON dbd_pg_test3(c)
WHERE c > 0 AND c < 45");
+ $dbh->commit();
+}
+
+my $correct_stats = {
+one => [
+ [ undef, 'public', 'dbd_pg_test1', undef, undef, undef, 'table', undef,
undef, undef, '0', '0', undef ],
+ [ undef, 'public', 'dbd_pg_test1', '0', undef, 'dbd_pg_test1_index_c',
'btree', 1, 'c', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test1', '0', undef, 'dbd_pg_test1_pk',
'btree', 1, 'a', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test1', '0', undef, 'dbd_pg_test1_uc1',
'btree', 1, 'b', 'A', '0', '1', undef ],
+],
+two => [
+ [ undef, 'public', 'dbd_pg_test2', undef, undef, undef, 'table', undef,
undef, undef, '0', '0', undef ],
+ [ undef, 'public', 'dbd_pg_test2', '0', undef, 'dbd_pg_test2_b_key',
'btree', 1, 'b', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test2', '0', undef, 'dbd_pg_test2_b_key',
'btree', 2, 'c', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test2', '0', undef, 'dbd_pg_test2_pkey',
'btree', 1, 'a', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test2', '0', undef, 'dbd_pg_test2_pkey',
'btree', 2, 'b', 'A', '0', '1', undef ],
+],
+three => [
+ [ undef, 'public', 'dbd_pg_test3', undef, undef, undef, 'table', undef,
undef, undef, '0', '0', undef ],
+ [ undef, 'public', 'dbd_pg_test3', '0', undef, 'dbd_pg_test3_index_b',
'btree', 1, 'b', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test3', '0', undef, 'dbd_pg_test3_pkey',
'btree', 1, 'a', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test3', '0', undef, 'dbd_pg_test3_pred',
'btree', 1, 'c', 'A', '0', '1', '((c > 0) AND (c < 45))' ],
+ [ undef, 'public', 'dbd_pg_test3', '1', undef, 'dbd_pg_test3_oid',
'btree', 1, 'oid', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test3', '1', undef, 'dbd_pg_test3_index_c',
'hashed', 1, 'c', 'A', '0', '0', undef ],
+],
+three_uo => [
+ [ undef, 'public', 'dbd_pg_test3', '0', undef, 'dbd_pg_test3_index_b',
'btree', 1, 'b', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test3', '0', undef, 'dbd_pg_test3_pkey',
'btree', 1, 'a', 'A', '0', '1', undef ],
+ [ undef, 'public', 'dbd_pg_test3', '0', undef, 'dbd_pg_test3_pred',
'btree', 1, 'c', 'A', '0', '1', '((c > 0) AND (c < 45))' ],
+],
+};
+
+if(!$got73) { # wipe out the schema names in the expected results above
+ foreach my $subset (values %$correct_stats) {
+ foreach (@$subset) {
+ $_->[1] = undef;
+ }
+ }
+}
+
+SKIP: {
+ skip qq{Cannot test statistics_info with schema arg on pre-7.3
servers.}, 3
+ if ! $got73;
+
+ my $stats;
+
+ $sth = $dbh->statistics_info(undef,'public','dbd_pg_test1',undef,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{one}, 'Correct stats output for
public.dbd_pg_test1');
+
+ $sth = $dbh->statistics_info(undef,'public','dbd_pg_test2',undef,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{two}, 'Correct stats output for
public.dbd_pg_test2');
+
+ $sth = $dbh->statistics_info(undef,'public','dbd_pg_test3',undef,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{three}, 'Correct stats output for
public.dbd_pg_test3');
+
+ $sth = $dbh->statistics_info(undef,'public','dbd_pg_test3',1,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{three_uo}, 'Correct stats output for
public.dbd_pg_test3 (unique only)');
+}
+
+{
+ my $stats;
+
+ $sth = $dbh->statistics_info(undef,undef,'dbd_pg_test1',undef,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{one}, 'Correct stats output for
dbd_pg_test1');
+
+ $sth = $dbh->statistics_info(undef,undef,'dbd_pg_test2',undef,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{two}, 'Correct stats output for
dbd_pg_test2');
+
+ $sth = $dbh->statistics_info(undef,undef,'dbd_pg_test3',undef,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{three}, 'Correct stats output for
dbd_pg_test3');
+
+ $sth = $dbh->statistics_info(undef,undef,'dbd_pg_test3',1,undef);
+ $stats = $sth->fetchall_arrayref;
+ is_deeply($stats, $correct_stats->{three_uo}, 'Correct stats output for
dbd_pg_test3 (unique only)');
+}
+
+# Clean everything up
+{
+ $dbh->do("DROP TABLE dbd_pg_test3");
+ $dbh->do("DROP TABLE dbd_pg_test2");
+ $dbh->do("DROP TABLE dbd_pg_test1");
+}
+
+#
# Test of the "foreign_key_info" database handle method
#