diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 6c1a211399ca55c6216d8763b7ee79c7ea467c22..c9fcb0231dd164191a2ebb1febfce4e4e70b0da0 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -5363,7 +5363,7 @@ getTables(Archive *fout, int *numTables) else selectDumpableTable(&tblinfo[i], dopt); tblinfo[i].interesting = tblinfo[i].dobj.dump; - + tblinfo[i].dummy_view = false; /* might get set during sort */ tblinfo[i].postponed_def = false; /* might get set during sort */ /* @@ -6165,16 +6165,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); @@ -13746,6 +13736,50 @@ createViewAsClause(Archive *fout, TableInfo *tbinfo) return result; } +/* + * 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) + appendPQExpBuffer(result, " COLLATE %s", + fmtQualifiedDumpable(coll)); + } + + appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j])); + } + + return result; +} + /* * dumpTableSchema * write the declaration (not data) of one user-defined table or view @@ -13780,6 +13814,10 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) { PQExpBuffer result; + /* + * Note: keep this code in sync with the is_view case in dumpRule() + */ + reltypename = "VIEW"; appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname); @@ -13790,17 +13828,22 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname); - if (nonemptyReloptions(tbinfo->reloptions)) + if (tbinfo->dummy_view) + result = createDummyViewAsClause(fout, tbinfo); + else { - appendPQExpBufferStr(q, " WITH ("); - fmtReloptionsArray(fout, q, tbinfo->reloptions, ""); - appendPQExpBufferChar(q, ')'); + if (nonemptyReloptions(tbinfo->reloptions)) + { + appendPQExpBufferStr(q, " WITH ("); + fmtReloptionsArray(fout, q, tbinfo->reloptions, ""); + 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"); } @@ -15426,6 +15469,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) { DumpOptions *dopt = fout->dopt; TableInfo *tbinfo = rinfo->ruletable; + bool is_view; PQExpBuffer query; PQExpBuffer cmd; PQExpBuffer delcmd; @@ -15445,6 +15489,12 @@ dumpRule(Archive *fout, RuleInfo *rinfo) if (!rinfo->separate) 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); + query = createPQExpBuffer(); cmd = createPQExpBuffer(); delcmd = createPQExpBuffer(); @@ -15452,30 +15502,60 @@ dumpRule(Archive *fout, RuleInfo *rinfo) qtabname = pg_strdup(fmtId(tbinfo->dobj.name)); - if (fout->remoteVersion >= 70300) + if (is_view) { - appendPQExpBuffer(query, - "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid) AS definition", - rinfo->dobj.catId.oid); + 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", + fmtQualifiedDumpable(tbinfo)); + if (nonemptyReloptions(tbinfo->reloptions)) + { + appendPQExpBufferStr(cmd, " WITH ("); + fmtReloptionsArray(fout, cmd, tbinfo->reloptions, ""); + 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 { - /* Rule name was unique before 7.3 ... */ - appendPQExpBuffer(query, - "SELECT pg_get_ruledef('%s') AS definition", - rinfo->dobj.name); - } + /* In the rule case, just print pg_get_ruledef's result verbatim */ + if (fout->remoteVersion >= 70300) + { + appendPQExpBuffer(query, + "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid) AS definition", + rinfo->dobj.catId.oid); + } + else + { + /* Rule name was unique before 7.3 ... */ + appendPQExpBuffer(query, + "SELECT pg_get_ruledef('%s') AS definition", + rinfo->dobj.name); + } - res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + 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); - } + 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 @@ -15501,22 +15581,29 @@ dumpRule(Archive *fout, RuleInfo *rinfo) } } - /* - * Apply view's reloptions when its ON SELECT rule is separate. - */ - if (nonemptyReloptions(rinfo->reloptions)) + if (is_view) + { + /* + * 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", + fmtQualifiedDumpable(tbinfo)); + result = createDummyViewAsClause(fout, tbinfo); + appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data); + destroyPQExpBuffer(result); + } + else { - appendPQExpBuffer(cmd, "ALTER VIEW %s SET (", + appendPQExpBuffer(delcmd, "DROP RULE %s ", + fmtId(rinfo->dobj.name)); + appendPQExpBuffer(delcmd, "ON %s;\n", fmtQualifiedDumpable(tbinfo)); - fmtReloptionsArray(fout, cmd, rinfo->reloptions, ""); - appendPQExpBufferStr(cmd, ");\n"); } - appendPQExpBuffer(delcmd, "DROP RULE %s ", - fmtId(rinfo->dobj.name)); - appendPQExpBuffer(delcmd, "ON %s;\n", - fmtQualifiedDumpable(tbinfo)); - appendPQExpBuffer(ruleprefix, "RULE %s ON", fmtId(rinfo->dobj.name)); @@ -15538,8 +15625,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 27f72e75bd31df09c2675162c7f8ffbd99de419d..748da5d59c2360184012211cffa4df7d5208c38f 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -226,6 +226,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 */ /* @@ -303,8 +304,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 48bc6685ca6d5c02c551e444d62b33ed11f33ac0..121b26c19a4f4873da9541cbcf02ef4d502f0c6e 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -874,6 +874,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 */ } /* @@ -893,27 +894,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); } /*