aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bin/pg_dump/pg_dump.c181
-rw-r--r--src/bin/pg_dump/pg_dump.h3
-rw-r--r--src/bin/pg_dump/pg_dump_sort.c15
3 files changed, 140 insertions, 59 deletions
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index bce26811739..9f59f53a7da 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5486,7 +5486,7 @@ getTables(Archive *fout, int *numTables)
tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
-
+ tblinfo[i].dummy_view = false; /* might get set during sort */
tblinfo[i].postponed_def = false; /* might get set during sort */
/*
@@ -6207,16 +6207,6 @@ getRules(Archive *fout, int *numRules)
}
else
ruleinfo[i].separate = true;
-
- /*
- * If we're forced to break a dependency loop by dumping a view as a
- * table and separate _RETURN rule, we'll move the view's reloptions
- * to the rule. (This is necessary because tables and views have
- * different valid reloptions, so we can't apply the options until the
- * backend knows it's a view.) Otherwise the rule's reloptions stay
- * NULL.
- */
- ruleinfo[i].reloptions = NULL;
}
PQclear(res);
@@ -13978,6 +13968,54 @@ createViewAsClause(Archive *fout, TableInfo *tbinfo)
}
/*
+ * Create a dummy AS clause for a view. This is used when the real view
+ * definition has to be postponed because of circular dependencies.
+ * We must duplicate the view's external properties -- column names and types
+ * (including collation) -- so that it works for subsequent references.
+ *
+ * This returns a new buffer which must be freed by the caller.
+ */
+static PQExpBuffer
+createDummyViewAsClause(Archive *fout, TableInfo *tbinfo)
+{
+ PQExpBuffer result = createPQExpBuffer();
+ int j;
+
+ appendPQExpBufferStr(result, "SELECT");
+
+ for (j = 0; j < tbinfo->numatts; j++)
+ {
+ if (j > 0)
+ appendPQExpBufferChar(result, ',');
+ appendPQExpBufferStr(result, "\n ");
+
+ appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
+
+ /*
+ * Must add collation if not default for the type, because CREATE OR
+ * REPLACE VIEW won't change it
+ */
+ if (OidIsValid(tbinfo->attcollation[j]))
+ {
+ CollInfo *coll;
+
+ coll = findCollationByOid(tbinfo->attcollation[j]);
+ if (coll)
+ {
+ /* always schema-qualify, don't try to be smart */
+ appendPQExpBuffer(result, " COLLATE %s.",
+ fmtId(coll->dobj.namespace->dobj.name));
+ appendPQExpBufferStr(result, fmtId(coll->dobj.name));
+ }
+ }
+
+ appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
+ }
+
+ return result;
+}
+
+/*
* dumpTableSchema
* write the declaration (not data) of one user-defined table or view
*/
@@ -14010,6 +14048,10 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
{
PQExpBuffer result;
+ /*
+ * Note: keep this code in sync with the is_view case in dumpRule()
+ */
+
reltypename = "VIEW";
/*
@@ -14026,17 +14068,22 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
tbinfo->dobj.catId.oid, false);
appendPQExpBuffer(q, "CREATE VIEW %s", fmtId(tbinfo->dobj.name));
- if (nonemptyReloptions(tbinfo->reloptions))
+ if (tbinfo->dummy_view)
+ result = createDummyViewAsClause(fout, tbinfo);
+ else
{
- appendPQExpBufferStr(q, " WITH (");
- appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
- appendPQExpBufferChar(q, ')');
+ if (nonemptyReloptions(tbinfo->reloptions))
+ {
+ appendPQExpBufferStr(q, " WITH (");
+ appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
+ appendPQExpBufferChar(q, ')');
+ }
+ result = createViewAsClause(fout, tbinfo);
}
- result = createViewAsClause(fout, tbinfo);
appendPQExpBuffer(q, " AS\n%s", result->data);
destroyPQExpBuffer(result);
- if (tbinfo->checkoption != NULL)
+ if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
appendPQExpBufferStr(q, ";\n");
@@ -15648,6 +15695,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
{
DumpOptions *dopt = fout->dopt;
TableInfo *tbinfo = rinfo->ruletable;
+ bool is_view;
PQExpBuffer query;
PQExpBuffer cmd;
PQExpBuffer delcmd;
@@ -15667,6 +15715,12 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
return;
/*
+ * If it's an ON SELECT rule, we want to print it as a view definition,
+ * instead of a rule.
+ */
+ is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
+
+ /*
* Make sure we are in proper schema.
*/
selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
@@ -15676,20 +15730,50 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
delcmd = createPQExpBuffer();
labelq = createPQExpBuffer();
- appendPQExpBuffer(query,
- "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid) AS definition",
- rinfo->dobj.catId.oid);
-
- res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
-
- if (PQntuples(res) != 1)
+ if (is_view)
{
- write_msg(NULL, "query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned\n",
- rinfo->dobj.name, tbinfo->dobj.name);
- exit_nicely(1);
+ PQExpBuffer result;
+
+ /*
+ * We need OR REPLACE here because we'll be replacing a dummy view.
+ * Otherwise this should look largely like the regular view dump code.
+ */
+ appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
+ fmtId(tbinfo->dobj.name));
+ if (nonemptyReloptions(tbinfo->reloptions))
+ {
+ appendPQExpBufferStr(cmd, " WITH (");
+ appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
+ appendPQExpBufferChar(cmd, ')');
+ }
+ result = createViewAsClause(fout, tbinfo);
+ appendPQExpBuffer(cmd, " AS\n%s", result->data);
+ destroyPQExpBuffer(result);
+ if (tbinfo->checkoption != NULL)
+ appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
+ tbinfo->checkoption);
+ appendPQExpBufferStr(cmd, ";\n");
}
+ else
+ {
+ /* In the rule case, just print pg_get_ruledef's result verbatim */
+ appendPQExpBuffer(query,
+ "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
+ rinfo->dobj.catId.oid);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ if (PQntuples(res) != 1)
+ {
+ write_msg(NULL, "query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned\n",
+ rinfo->dobj.name, tbinfo->dobj.name);
+ exit_nicely(1);
+ }
- printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
+ printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
+
+ PQclear(res);
+ }
/*
* Add the command to alter the rules replication firing semantics if it
@@ -15716,25 +15800,34 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
}
/*
- * Apply view's reloptions when its ON SELECT rule is separate.
+ * DROP must be fully qualified in case same name appears in pg_catalog
*/
- if (nonemptyReloptions(rinfo->reloptions))
+ if (is_view)
{
- appendPQExpBuffer(cmd, "ALTER VIEW %s SET (",
+ /*
+ * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
+ * REPLACE VIEW to replace the rule with something with minimal
+ * dependencies.
+ */
+ PQExpBuffer result;
+
+ appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delcmd, "%s",
+ fmtId(tbinfo->dobj.name));
+ result = createDummyViewAsClause(fout, tbinfo);
+ appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
+ destroyPQExpBuffer(result);
+ }
+ else
+ {
+ appendPQExpBuffer(delcmd, "DROP RULE %s ",
+ fmtId(rinfo->dobj.name));
+ appendPQExpBuffer(delcmd, "ON %s.",
+ fmtId(tbinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delcmd, "%s;\n",
fmtId(tbinfo->dobj.name));
- appendReloptionsArrayAH(cmd, rinfo->reloptions, "", fout);
- appendPQExpBufferStr(cmd, ");\n");
}
-
- /*
- * DROP must be fully qualified in case same name appears in pg_catalog
- */
- appendPQExpBuffer(delcmd, "DROP RULE %s ",
- fmtId(rinfo->dobj.name));
- appendPQExpBuffer(delcmd, "ON %s.",
- fmtId(tbinfo->dobj.namespace->dobj.name));
- appendPQExpBuffer(delcmd, "%s;\n",
- fmtId(tbinfo->dobj.name));
appendPQExpBuffer(labelq, "RULE %s",
fmtId(rinfo->dobj.name));
@@ -15761,8 +15854,6 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
tbinfo->rolname,
rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
- PQclear(res);
-
free(tag);
destroyPQExpBuffer(query);
destroyPQExpBuffer(cmd);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 642c4d5f7f9..f3e59771783 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -287,6 +287,7 @@ typedef struct _tableInfo
int relpages; /* table's size in pages (from pg_class) */
bool interesting; /* true if need to collect more data */
+ bool dummy_view; /* view's real definition must be postponed */
bool postponed_def; /* matview must be postponed into post-data */
/*
@@ -364,8 +365,6 @@ typedef struct _ruleInfo
char ev_enabled;
bool separate; /* TRUE if must dump as separate item */
/* separate is always true for non-ON SELECT rules */
- char *reloptions; /* options specified by WITH (...) */
- /* reloptions is only set if we need to dump the options with the rule */
} RuleInfo;
typedef struct _triggerInfo
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 5b96334f496..996642384fd 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -786,6 +786,7 @@ repairViewRuleLoop(DumpableObject *viewobj,
{
/* remove rule's dependency on view */
removeObjectDependency(ruleobj, viewobj->dumpId);
+ /* flags on the two objects are already set correctly for this case */
}
/*
@@ -805,27 +806,17 @@ repairViewRuleMultiLoop(DumpableObject *viewobj,
{
TableInfo *viewinfo = (TableInfo *) viewobj;
RuleInfo *ruleinfo = (RuleInfo *) ruleobj;
- int i;
/* remove view's dependency on rule */
removeObjectDependency(viewobj, ruleobj->dumpId);
- /* pretend view is a plain table and dump it that way */
- viewinfo->relkind = 'r'; /* RELKIND_RELATION */
+ /* mark view to be printed with a dummy definition */
+ viewinfo->dummy_view = true;
/* mark rule as needing its own dump */
ruleinfo->separate = true;
- /* move any reloptions from view to rule */
- if (viewinfo->reloptions)
- {
- ruleinfo->reloptions = viewinfo->reloptions;
- viewinfo->reloptions = NULL;
- }
/* put back rule's dependency on view */
addObjectDependency(ruleobj, viewobj->dumpId);
/* now that rule is separate, it must be post-data */
addObjectDependency(ruleobj, postDataBoundId);
- /* also, any triggers on the view must be dumped after the rule */
- for (i = 0; i < viewinfo->numTriggers; i++)
- addObjectDependency(&(viewinfo->triggers[i].dobj), ruleobj->dumpId);
}
/*