From 10aa877ec41f136b6d562179f93f274b04f38a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Wed, 14 Mar 2018 16:23:58 +0100 Subject: [PATCH 1/2] Detect view definition changes --- src/view.c | 6 ++++++ test/from-view.sql | 2 ++ test/to-view.sql | 2 ++ 3 files changed, 10 insertions(+) diff --git a/src/view.c b/src/view.c index 5bdf2ee..9609901 100644 --- a/src/view.c +++ b/src/view.c @@ -358,6 +358,12 @@ dumpAlterView(FILE *output, PQLView *a, PQLView *b) } } + if (strcmp(a->viewdef, b->viewdef) != 0) + { + dumpDropView(output, a); + dumpCreateView(output, b); + } + /* comment */ if (options.comment) { diff --git a/test/from-view.sql b/test/from-view.sql index ed33032..ec77caa 100644 --- a/test/from-view.sql +++ b/test/from-view.sql @@ -3,3 +3,5 @@ CREATE VIEW same_view_1 AS SELECT prod_id, title, price FROM products WHERE comm CREATE VIEW same_view_2 AS SELECT orderdate, COUNT(*) AS total_day FROM orders GROUP BY orderdate; ALTER VIEW same_view_2 SET (security_barrier=on); + +CREATE VIEW same_view_3 AS SELECT orderdate, COUNT(*) AS total_day FROM orders GROUP BY orderdate; diff --git a/test/to-view.sql b/test/to-view.sql index fb16b8c..b0df80a 100644 --- a/test/to-view.sql +++ b/test/to-view.sql @@ -3,3 +3,5 @@ CREATE VIEW same_view_1 AS SELECT prod_id, title, price FROM products WHERE comm ALTER VIEW same_view_1 SET (security_barrier=on, check_option=cascaded); CREATE VIEW same_view_2 AS SELECT orderdate, COUNT(*) AS total_day FROM orders GROUP BY orderdate; + +CREATE VIEW same_view_3 AS SELECT orderdate, COUNT(*) AS totals_by_day FROM orders GROUP BY orderdate; From e259a7b4006709b6c0430aafdfab820825538655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Fri, 16 Mar 2018 10:21:48 +0100 Subject: [PATCH 2/2] Do not drop/create views if other objects depend on them --- src/view.c | 26 +++++++++++++++++++++----- src/view.h | 2 ++ test/from-view.sql | 2 ++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/view.c b/src/view.c index e338091..4aa09f3 100644 --- a/src/view.c +++ b/src/view.c @@ -42,17 +42,17 @@ getViews(PGconn *c, int *n) if (PQserverVersion(c) >= 90300) { res = PQexec(c, - "SELECT c.oid, n.nspname, c.relname, pg_get_viewdef(c.oid) AS viewdef, array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, CASE WHEN 'check_option=local' = ANY(c.reloptions) THEN 'LOCAL'::text WHEN 'check_option=cascaded' = ANY(c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, obj_description(c.oid, 'pg_class') AS description, pg_get_userbyid(c.relowner) AS relowner FROM pg_class c INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE relkind = 'v' AND nspname !~ '^pg_' AND nspname <> 'information_schema' AND NOT EXISTS(SELECT 1 FROM pg_depend d WHERE c.oid = d.objid AND d.deptype = 'e') ORDER BY nspname, relname"); + "SELECT c.oid, n.nspname, c.relname, pg_get_viewdef(c.oid) AS viewdef, array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, CASE WHEN 'check_option=local' = ANY(c.reloptions) THEN 'LOCAL'::text WHEN 'check_option=cascaded' = ANY(c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, obj_description(c.oid, 'pg_class') AS description, pg_get_userbyid(c.relowner) AS relowner, EXISTS(SELECT 1 FROM pg_depend d WHERE d.refobjid = c.oid AND d.deptype = 'n' AND NOT EXISTS(SELECT 1 FROM pg_depend d2 WHERE d2.refobjid = d.refobjid AND d2.objid = d.objid AND deptype IN ('i', 'a'))) AS has_dependants FROM pg_class c INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE relkind = 'v' AND nspname !~ '^pg_' AND nspname <> 'information_schema' AND NOT EXISTS(SELECT 1 FROM pg_depend d WHERE c.oid = d.objid AND d.deptype = 'e') ORDER BY nspname, relname"); } else if (PQserverVersion(c) >= 90100) /* extension support */ { res = PQexec(c, - "SELECT c.oid, n.nspname, c.relname, pg_get_viewdef(c.oid) AS viewdef, array_to_string(c.reloptions, ', ') AS reloptions, CASE WHEN 'check_option=local' = ANY(c.reloptions) THEN 'LOCAL'::text WHEN 'check_option=cascaded' = ANY(c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, obj_description(c.oid, 'pg_class') AS description, pg_get_userbyid(c.relowner) AS relowner FROM pg_class c INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE relkind = 'v' AND nspname !~ '^pg_' AND nspname <> 'information_schema' AND NOT EXISTS(SELECT 1 FROM pg_depend d WHERE c.oid = d.objid AND d.deptype = 'e') ORDER BY nspname, relname"); + "SELECT c.oid, n.nspname, c.relname, pg_get_viewdef(c.oid) AS viewdef, array_to_string(c.reloptions, ', ') AS reloptions, CASE WHEN 'check_option=local' = ANY(c.reloptions) THEN 'LOCAL'::text WHEN 'check_option=cascaded' = ANY(c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, obj_description(c.oid, 'pg_class') AS description, pg_get_userbyid(c.relowner) AS relowner, EXISTS(SELECT 1 FROM pg_depend d WHERE d.refobjid = c.oid AND d.deptype = 'n' AND NOT EXISTS(SELECT 1 FROM pg_depend d2 WHERE d2.refobjid = d.refobjid AND d2.objid = d.objid AND deptype IN ('i', 'a'))) AS has_dependants FROM pg_class c INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE relkind = 'v' AND nspname !~ '^pg_' AND nspname <> 'information_schema' AND NOT EXISTS(SELECT 1 FROM pg_depend d WHERE c.oid = d.objid AND d.deptype = 'e') ORDER BY nspname, relname"); } else { res = PQexec(c, - "SELECT c.oid, n.nspname, c.relname, pg_get_viewdef(c.oid) AS viewdef, array_to_string(c.reloptions, ', ') AS reloptions, CASE WHEN 'check_option=local' = ANY(c.reloptions) THEN 'LOCAL'::text WHEN 'check_option=cascaded' = ANY(c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, obj_description(c.oid, 'pg_class') AS description, pg_get_userbyid(c.relowner) AS relowner FROM pg_class c INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE relkind = 'v' AND nspname !~ '^pg_' AND nspname <> 'information_schema' ORDER BY nspname, relname"); + "SELECT c.oid, n.nspname, c.relname, pg_get_viewdef(c.oid) AS viewdef, array_to_string(c.reloptions, ', ') AS reloptions, CASE WHEN 'check_option=local' = ANY(c.reloptions) THEN 'LOCAL'::text WHEN 'check_option=cascaded' = ANY(c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, obj_description(c.oid, 'pg_class') AS description, pg_get_userbyid(c.relowner) AS relowner, EXISTS(SELECT 1 FROM pg_depend d WHERE d.refobjid = c.oid AND d.deptype = 'n' AND NOT EXISTS(SELECT 1 FROM pg_depend d2 WHERE d2.refobjid = d.refobjid AND d2.objid = d.objid AND deptype IN ('i', 'a'))) AS has_dependants FROM pg_class c INNER JOIN pg_namespace n ON (c.relnamespace = n.oid) WHERE relkind = 'v' AND nspname !~ '^pg_' AND nspname <> 'information_schema' ORDER BY nspname, relname"); } if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -94,6 +94,8 @@ getViews(PGconn *c, int *n) v[i].owner = strdup(PQgetvalue(res, i, PQfnumber(res, "relowner"))); + v[i].has_dependants = (PQgetvalue(res, i, PQfnumber(res, "has_dependants"))[0] == 't'); + /* * Security labels are not assigned here (see getViewSecurityLabels), * but default values are essential to avoid having trouble in @@ -358,10 +360,24 @@ dumpAlterView(FILE *output, PQLView *a, PQLView *b) } } + /* + * Compare view definitions. + * Note: PostgreSQL 9.3 changed the way pg_get_viewdef(...) deals with indentation and line wrapping. + * This may result in false positives when comparing views using < 9.3 and >= 9.3. + */ if (strcmp(a->viewdef, b->viewdef) != 0) { - dumpDropView(output, a); - dumpCreateView(output, b); + if (!a->has_dependants) + { + dumpDropView(output, a); + dumpCreateView(output, b); + } + else + { + logWarning("view \"%s\".\"%s\" changed definition, but cannot be replaced automatically; some other objects depend on it", a->obj.schemaname, a->obj.objectname); + fprintf(output, "\n\n"); + fprintf(output, "-- view %s.%s changed definition;", a->obj.schemaname, a->obj.objectname); + } } /* comment */ diff --git a/src/view.h b/src/view.h index e193be9..2e15d0b 100644 --- a/src/view.h +++ b/src/view.h @@ -22,6 +22,8 @@ typedef struct PQLView char *comment; char *owner; + bool has_dependants; + /* security labels */ PQLSecLabel *seclabels; int nseclabels; diff --git a/test/from-view.sql b/test/from-view.sql index ec77caa..3b5a76e 100644 --- a/test/from-view.sql +++ b/test/from-view.sql @@ -5,3 +5,5 @@ CREATE VIEW same_view_2 AS SELECT orderdate, COUNT(*) AS total_day FROM orders G ALTER VIEW same_view_2 SET (security_barrier=on); CREATE VIEW same_view_3 AS SELECT orderdate, COUNT(*) AS total_day FROM orders GROUP BY orderdate; + +-- CREATE VIEW same_view_4 AS SELECT * FROM same_view_3;