diff options
-rw-r--r-- | src/bin/pg_dump/pg_dump.c | 181 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_dump.h | 3 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_dump_sort.c | 15 |
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); } /* |