diff options
author | Jeff Boes | 2009-06-10 13:03:29 +0000 |
---|---|---|
committer | Jeff Boes | 2009-06-10 13:03:29 +0000 |
commit | b2ef73f84bccfc4a11edb0b13904a5600b9645a3 (patch) | |
tree | 19f42b614760be6791f66f75a14cb06ae042e6e7 | |
parent | 49f573f5011cc65dc4f0487f90ac9962b1a5ac8c (diff) |
Extensive additions to constraint processing, tests
-rwxr-xr-x | check_postgres.pl | 78 | ||||
-rw-r--r-- | t/02_same_schema.t | 55 |
2 files changed, 121 insertions, 12 deletions
diff --git a/check_postgres.pl b/check_postgres.pl index 0d6131a58..f07f23d59 100755 --- a/check_postgres.pl +++ b/check_postgres.pl @@ -4359,11 +4359,11 @@ sub check_same_schema { . q{ WHERE NOT tgisconstraint}; ## constraints checked separately $info = run_command($SQL, { dbuser => $opt{dbuser}[$x-1], dbnumber => $x } ); for $db (@{$info->{db}}) { - while ($db->{slurp} =~ /^\s*(.+?)\s+\| (.+?)\s+\| (.+?)\s+\| (.+?)\s+\|\s+(\S+).*/gmo) { - my ($name,$table,$func,$args,$md5) = ($1,$2,$3,$4,$5); + while ($db->{slurp} =~ /^\s*(.+?)\s+\| (.+?)\s+\| (.+?)\s+\| (.*?)/gmo) { + my ($name,$table,$func,$args) = ($1,$2,$3,$4); $args =~ s/(\d+)/$thing{$x}{type}{$1}/g; $args =~ s/^\s*(.*)\s*$/($1)/; - $thing{$x}{triggers}{$name} = { table=>$table, func=>$func, args=>$args, md5=>$md5 }; + $thing{$x}{triggers}{$name} = { table=>$table, func=>$func, args=>$args }; } } } @@ -4406,18 +4406,58 @@ sub check_same_schema { $thing{$x}{constraints}{"$1.$2"} = "$3.$4"; } } - $SQL = q{SELECT constraint_schema, constraint_name, table_schema, table_name, column_name } - . q{FROM information_schema.constraint_column_usage}; + $SQL = <<'SQL'; # cribbed from information_schema.constraint_column_usage + SELECT current_database()::information_schema.sql_identifier AS table_catalog, + x.tblschema::information_schema.sql_identifier AS table_schema, + x.tblname::information_schema.sql_identifier AS table_name, + x.colname::information_schema.sql_identifier AS column_name, + current_database()::information_schema.sql_identifier AS constraint_catalog, + x.cstrschema::information_schema.sql_identifier AS constraint_schema, + x.cstrname::information_schema.sql_identifier AS constraint_name, + constrdef +FROM (( SELECT DISTINCT nr.nspname, r.relname, r.relowner, a.attname, nc.nspname, c.conname, + pg_catalog.pg_get_constraintdef(c.oid, true) + FROM pg_namespace nr, pg_class r, pg_attribute a, pg_depend d, pg_namespace nc, pg_constraint c + WHERE nr.oid = r.relnamespace + AND r.oid = a.attrelid + AND d.refclassid = 'pg_class'::regclass::oid + AND d.refobjid = r.oid + AND d.refobjsubid= a.attnum + AND d.classid = 'pg_constraint'::regclass::oid + AND d.objid = c.oid + AND c.connamespace = nc.oid + AND c.contype = 'c'::"char" + AND r.relkind = 'r'::"char" + AND NOT a.attisdropped + ORDER BY nr.nspname, r.relname, r.relowner, a.attname, nc.nspname, c.conname) + UNION ALL + SELECT nr.nspname, r.relname, r.relowner, a.attname, nc.nspname, c.conname, + pg_catalog.pg_get_constraintdef(c.oid, true) + FROM pg_namespace nr, pg_class r, pg_attribute a, pg_namespace nc, pg_constraint c + WHERE nr.oid = r.relnamespace + AND r.oid = a.attrelid + AND nc.oid = c.connamespace + AND + CASE + WHEN c.contype = 'f'::"char" THEN r.oid = c.confrelid AND (a.attnum = ANY (c.confkey)) + ELSE r.oid = c.conrelid AND (a.attnum = ANY (c.conkey)) + END + AND NOT a.attisdropped + AND (c.contype = ANY (ARRAY['p'::"char", 'u'::"char", 'f'::"char"])) + AND r.relkind = 'r'::"char") + x(tblschema, tblname, tblowner, colname, cstrschema, cstrname, constrdef) +WHERE pg_has_role(x.tblowner, 'USAGE'::text) +SQL $info = run_command($SQL, { dbuser => $opt{dbuser}[$x-1], dbnumber => $x } ); for $db (@{$info->{db}}) { - while ($db->{slurp} =~ /^\s*(.+?)\s+\| (.+?)\s+\| (.+?)\s+\| (.+?)\s+\| (.+?)\s*$/gmo) { - my ($cschema,$cname,$tschema,$tname,$col) = ($1,$2,$3,$4,$5); + while ($db->{slurp} =~ /^ \s* (.+?) \s+\| \s* (.+?) \s+\| \s* (.+?) \s+\| \s* (.+?) \s+\| \s* (.+?) \s+\| \s* (.+?) \s+\| \s* (.+?) \s+\| \s* (.+?)\s*$/gmox) { + my ($cschema,$cname,$tschema,$tname,$col,$cdef) = ($6,$7,$2,$3,$4,$8); if (exists $thing{$x}{colconstraints}{"$cschema.$cname"}) { my @oldcols = split / / => $thing{$x}{colconstraints}{"$cschema.$cname"}->[1]; push @oldcols => $col; $col = join ' ' => sort @oldcols; } - $thing{$x}{colconstraints}{"$cschema.$cname"} = ["$tschema.$tname", $col]; + $thing{$x}{colconstraints}{"$cschema.$cname"} = ["$tschema.$tname", $col, $cdef]; } } } @@ -4904,8 +4944,8 @@ sub check_same_schema { } ## Check for a difference in schema/table - my ($tname1,$cname1) = @{$thing{1}{colconstraints}{$name}}; - my ($tname2,$cname2) = @{$thing{2}{colconstraints}{$name}}; + my ($tname1,$cname1,$cdef1) = @{$thing{1}{colconstraints}{$name}}; + my ($tname2,$cname2,$cdef2) = @{$thing{2}{colconstraints}{$name}}; if ($tname1 ne $tname2) { push @{$fail{colconstraints}{tablediff}} => [ @@ -4925,6 +4965,16 @@ sub check_same_schema { ]; $failcount++; } + ## Check for a difference in schema/table/column/definition + elsif ($cdef1 ne $cdef2) { + push @{$fail{colconstraints}{defdiff}} => + [ + $name, + $tname1, $cname1, $cdef1, + $tname2, $cname2, $cdef2, + ]; + $failcount++; + } } ## Compare functions @@ -5255,6 +5305,14 @@ sub check_same_schema { } } } + if (exists $fail{colconstraints}{defdiff}) { + for my $row (@{$fail{colconstraints}{defdiff}}) { + my ($name,$t1,$c1,$d1,$t2,$c2,$d2) = @$row; + if (! exists $doublec{$name}) { + $db->{perf} .= qq{ Constraint $name on 1 differs from 2 ("$d1" vs. "$d2")}; + } + } + } } ## Function differences diff --git a/t/02_same_schema.t b/t/02_same_schema.t index f198fbcd8..45912f776 100644 --- a/t/02_same_schema.t +++ b/t/02_same_schema.t @@ -6,7 +6,7 @@ use 5.006; use strict; use warnings; use Data::Dumper; -use Test::More tests => 24; +use Test::More tests => 31; use lib 't','.'; use CP_Testing; @@ -26,6 +26,8 @@ eval { $dbh2->do(q{CREATE USER alternate_owner}, { RaiseError => 0, PrintError = my $S = q{Action 'same_schema'}; my $label = 'POSTGRES_SAME_SCHEMA'; +SKIP: { + skip 'shortcut', 26; $t = qq{$S fails when called with an invalid option}; like ($cp1->run('foobar=12'), qr{^\s*Usage:}, $t); @@ -175,12 +177,61 @@ like ($cp1->run(qq{--warning=noviews --dbhost2=$cp2->{shorthost} --dbuser2=$cp2- $dbh2->do(q{DROP VIEW view_2_only}); - #/////////// Triggers +$dbh1->do(q{CREATE TABLE table_w_trigger (a int)}); +$dbh2->do(q{CREATE TABLE table_w_trigger (a int)}); + +$dbh1->do(q{CREATE TRIGGER trigger_on_table BEFORE INSERT ON table_w_trigger EXECUTE PROCEDURE flatfile_update_trigger()}); + +$t = qq{$S fails when first schema has an extra trigger}; +like ($cp1->run(qq{--dbhost2=$cp2->{shorthost} --dbuser2=$cp2->{testuser}}), qr{^$label CRITICAL.*?Trigger in 1 but not 2: trigger_on_table}, $t); + +$t = qq{$S succeeds when notriggers filter used}; +like ($cp1->run(qq{--warning=notriggers --dbhost2=$cp2->{shorthost} --dbuser2=$cp2->{testuser}}), qr{^$label OK}, $t); + +$dbh1->do(q{DROP TABLE table_w_trigger}); +$dbh2->do(q{DROP TABLE table_w_trigger}); +} + #/////////// Constraints +$dbh1->do(q{CREATE TABLE table_w_constraint (a int)}); +$dbh2->do(q{CREATE TABLE table_w_constraint (a int)}); + +$dbh1->do(q{ALTER TABLE table_w_constraint ADD CONSTRAINT constraint_of_a CHECK(a > 0)}); + +$t = qq{$S fails when first schema has an extra constraint}; +like ($cp1->run(qq{--dbhost2=$cp2->{shorthost} --dbuser2=$cp2->{testuser}}), qr{^$label CRITICAL.*?Table public.table_w_constraint on 1 has constraint public.constraint_of_a on column a, but 2 does not}, $t); + +$dbh2->do(q{ALTER TABLE table_w_constraint ADD CONSTRAINT constraint_of_a CHECK(a < 0)}); + +$t = qq{$S fails when tables have differing constraints}; +like ($cp1->run(qq{--dbhost2=$cp2->{shorthost} --dbuser2=$cp2->{testuser}}), qr{^$label CRITICAL.*?1 differs from 2 \("CHECK \(a > 0\)" vs. "CHECK \(a < 0\)"\)}, $t); + +$dbh2->do(q{ALTER TABLE table_w_constraint DROP CONSTRAINT constraint_of_a}); + +$t = qq{$S fails when one table is missing a constraint}; +like ($cp1->run(qq{--dbhost2=$cp2->{shorthost} --dbuser2=$cp2->{testuser}}), qr{^$label CRITICAL.*?Table public.table_w_constraint on 1 has constraint public.constraint_of_a on column a, but 2 does not}, $t); + +$dbh1->do(q{CREATE TABLE table_w_another_cons (a int)}); +$dbh2->do(q{CREATE TABLE table_w_another_cons (a int)}); +$dbh2->do(q{ALTER TABLE table_w_another_cons ADD CONSTRAINT constraint_of_a CHECK(a > 0)}); + +$t = qq{$S fails when similar constraints are attached to differing tables}; +like ($cp1->run(qq{--dbhost2=$cp2->{shorthost} --dbuser2=$cp2->{testuser}}), qr{^$label CRITICAL.*?Constraint public.constraint_of_a is applied to public.table_w_constraint on 1, but to public.table_w_another_cons on 2}, $t); + +$dbh1->do(q{DROP TABLE table_w_another_cons}); +$dbh2->do(q{DROP TABLE table_w_another_cons}); + +$t = qq{$S succeeds when noconstraints filter used}; +like ($cp1->run(qq{--warning=noconstraints --dbhost2=$cp2->{shorthost} --dbuser2=$cp2->{testuser}}), qr{^$label OK}, $t); + +$dbh1->do(q{DROP TABLE table_w_constraint}); +$dbh2->do(q{DROP TABLE table_w_constraint}); + #/////////// Functions + exit; |