pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feed From: Alvaro Herrera <alvherre@2ndquadrant.com>
To: Pg Hackers <pgsql-hackers@lists.postgresql.org>
Subject: more ALTER .. DEPENDS ON EXTENSION fixes
Date: Mon, 17 Feb 2020 19:53:33 -0300
Message-ID: <20200217225333.GA30974@alvherre.pgsql> (raw )
ALTER ... DEPENDS ON EXTENSION (dependencies of type 'x' on an
extension) was found to have a few problems. One was fixed as
CVE-2020-1720. Other issues:
* pg_dump does not reproduce database state correctly.
The attached 0000 fixes it.
* More than one 'x' dependencies are allowed for the same object on the
same extension. That's useless and polluting, so should be prevented.
* There's no way to remove an 'x' dependency.
I'll send patches for the other two issues as replies later. (I
discovered an issue in my 0001, for the second one, just as I was
sending.)
--
Álvaro Herrera
Attachments:
0001-Add-pg_dump-support-for-ALTER-object-DEPENDS-ON-EXTE.patch (text/x-diff, 10.1K) download
From 86cd061aff0a1c39ccfdc945a42fe88612ce2182 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 29 Oct 2019 23:19:13 -0300
Subject: [PATCH] Add pg_dump support for ALTER <object> DEPENDS ON EXTENSION
Dumping/restoring objects so marked would lose the markings, forcing
users to re-add them manually after the fact. (This affects pg_upgrade
too). Have pg_dump emit commands to preserve them.
---
src/bin/pg_dump/common.c | 1 +
src/bin/pg_dump/pg_dump.c | 107 ++++++++++++++++++--
src/bin/pg_dump/pg_dump.h | 1 +
src/test/modules/test_pg_dump/t/001_base.pl | 32 ++++++
4 files changed, 133 insertions(+), 8 deletions(-)
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 69508ec9b3..4ea59f5494 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -549,6 +549,7 @@ AssignDumpId(DumpableObject *dobj)
dobj->namespace = NULL; /* may be set later */
dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */
dobj->ext_member = false; /* default assumption */
+ dobj->depends_on_ext = false; /* default assumption */
dobj->dependencies = NULL;
dobj->nDeps = 0;
dobj->allocDeps = 0;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ec3e2c63b0..eeb0d90b17 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4275,6 +4275,55 @@ dumpSubscription(Archive *fout, SubscriptionInfo *subinfo)
free(qsubname);
}
+/*
+ * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
+ * the object needs.
+ */
+static void
+append_depends_on_extension(Archive *fout,
+ PQExpBuffer create,
+ DumpableObject *dobj,
+ const char *catalog,
+ const char *keyword,
+ const char *objname)
+{
+ if (dobj->depends_on_ext)
+ {
+ char *nm;
+ PGresult *res;
+ PQExpBuffer query;
+ int ntups;
+ int i_extname;
+ int i;
+
+ /* dodge fmtId() non-reentrancy */
+ nm = pg_strdup(objname);
+
+ query = createPQExpBuffer();
+ appendPQExpBuffer(query,
+ "SELECT e.extname "
+ "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
+ "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
+ "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
+ "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
+ catalog,
+ dobj->catId.oid);
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ ntups = PQntuples(res);
+ i_extname = PQfnumber(res, "extname");
+ for (i = 0; i < ntups; i++)
+ {
+ appendPQExpBuffer(create, "ALTER %s %s DEPENDS ON EXTENSION %s;\n",
+ keyword, nm,
+ fmtId(PQgetvalue(res, i, i_extname)));
+ }
+
+ PQclear(res);
+ pg_free(nm);
+ }
+}
+
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -12145,6 +12194,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
appendPQExpBuffer(q, "\n %s;\n", asPart->data);
+ append_depends_on_extension(fout, q, &finfo->dobj,
+ "pg_catalog.pg_proc", keyword,
+ psprintf("%s.%s",
+ fmtId(finfo->dobj.namespace->dobj.name),
+ funcsig));
+
if (dopt->binary_upgrade)
binary_upgrade_extension_member(q, &finfo->dobj,
keyword, funcsig,
@@ -15855,6 +15910,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
else
appendPQExpBufferStr(q, ";\n");
+ /* Materialized views can depend on extensions */
+ if (tbinfo->relkind == RELKIND_MATVIEW)
+ append_depends_on_extension(fout, q, &tbinfo->dobj,
+ "pg_catalog.pg_class",
+ tbinfo->relkind == RELKIND_MATVIEW ?
+ "MATERIALIZED VIEW" : "INDEX",
+ qualrelname);
+
/*
* in binary upgrade mode, update the catalog with any missing values
* that might be present.
@@ -16359,6 +16422,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo)
PQExpBuffer q;
PQExpBuffer delq;
char *qindxname;
+ char *qqindxname;
if (dopt->dataOnly)
return;
@@ -16367,6 +16431,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo)
delq = createPQExpBuffer();
qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
+ qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
/*
* If there's an associated constraint, don't dump the index per se, but
@@ -16419,8 +16484,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo)
for (j = 0; j < nstatcols; j++)
{
- appendPQExpBuffer(q, "ALTER INDEX %s ",
- fmtQualifiedDumpable(indxinfo));
+ appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
/*
* Note that this is a column number, so no quotes should be
@@ -16433,6 +16497,11 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo)
}
}
+ /* Indexes can depend on extensions */
+ append_depends_on_extension(fout, q, &indxinfo->dobj,
+ "pg_catalog.pg_class",
+ "INDEX", qqindxname);
+
/* If the index defines identity, we need to record that. */
if (indxinfo->indisreplident)
{
@@ -16443,8 +16512,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo)
qindxname);
}
- appendPQExpBuffer(delq, "DROP INDEX %s;\n",
- fmtQualifiedDumpable(indxinfo));
+ appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
@@ -16475,6 +16543,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo)
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
free(qindxname);
+ free(qqindxname);
}
/*
@@ -16713,6 +16782,11 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
fmtId(indxinfo->dobj.name));
}
+ /* Indexes can depend on extensions */
+ append_depends_on_extension(fout, q, &indxinfo->dobj,
+ "pg_catalog.pg_class", "INDEX",
+ fmtQualifiedDumpable(indxinfo));
+
appendPQExpBuffer(delq, "ALTER TABLE ONLY %s ",
fmtQualifiedDumpable(tbinfo));
appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
@@ -17232,6 +17306,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
PQExpBuffer query;
PQExpBuffer delqry;
PQExpBuffer trigprefix;
+ PQExpBuffer trigidentity;
char *qtabname;
char *tgargs;
size_t lentgargs;
@@ -17249,13 +17324,14 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
query = createPQExpBuffer();
delqry = createPQExpBuffer();
trigprefix = createPQExpBuffer();
+ trigidentity = createPQExpBuffer();
qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
- appendPQExpBuffer(delqry, "DROP TRIGGER %s ",
- fmtId(tginfo->dobj.name));
- appendPQExpBuffer(delqry, "ON %s;\n",
- fmtQualifiedDumpable(tbinfo));
+ appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
+ appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
+
+ appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
if (tginfo->tgdef)
{
@@ -17374,6 +17450,11 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
appendPQExpBufferStr(query, ");\n");
}
+ /* Triggers can depend on extensions */
+ append_depends_on_extension(fout, query, &tginfo->dobj,
+ "pg_catalog.pg_trigger", "TRIGGER",
+ trigidentity->data);
+
if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
{
appendPQExpBuffer(query, "\nALTER TABLE %s ",
@@ -17422,6 +17503,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
destroyPQExpBuffer(query);
destroyPQExpBuffer(delqry);
destroyPQExpBuffer(trigprefix);
+ destroyPQExpBuffer(trigidentity);
free(qtabname);
}
@@ -18071,6 +18153,15 @@ getDependencies(Archive *fout)
continue;
}
+ /*
+ * For 'x' dependencies, mark the object for later; we still add the
+ * normal dependency, for possible ordering purposes. Currently
+ * pg_dump_sort.c knows to put extensions ahead of all object types
+ * that could possibly depend on them, but this is safer.
+ */
+ if (deptype == 'x')
+ dobj->depends_on_ext = true;
+
/*
* Ordinarily, table rowtypes have implicit dependencies on their
* tables. However, for a composite type the implicit dependency goes
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 21004e5078..585a1f9257 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -132,6 +132,7 @@ typedef struct _dumpableObject
DumpComponents dump; /* bitmask of components to dump */
DumpComponents dump_contains; /* as above, but for contained objects */
bool ext_member; /* true if object is member of extension */
+ bool depends_on_ext; /* true if object depends on an extension */
DumpId *dependencies; /* dumpIds of objects this one depends on */
int nDeps; /* number of valid dependencies */
int allocDeps; /* allocated size of dependencies[] */
diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl
index fb4ecf8aca..ae120a5ee3 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -523,6 +523,38 @@ my %tests = (
like => { binary_upgrade => 1, },
},
+ 'ALTER INDEX pkey DEPENDS ON extension' => {
+ create_order => 11,
+ create_sql =>
+ 'CREATE TABLE regress_pg_dump_schema.extdependtab (col1 integer primary key, col2 int);
+ CREATE INDEX ON regress_pg_dump_schema.extdependtab (col2);
+ ALTER INDEX regress_pg_dump_schema.extdependtab_col2_idx DEPENDS ON EXTENSION test_pg_dump;
+ ALTER INDEX regress_pg_dump_schema.extdependtab_pkey DEPENDS ON EXTENSION test_pg_dump;',
+ regexp => qr/^
+ \QALTER INDEX regress_pg_dump_schema.extdependtab_pkey DEPENDS ON EXTENSION test_pg_dump;\E\n
+ /xms,
+ like => {%pgdump_runs},
+ unlike => {
+ data_only => 1,
+ pg_dumpall_globals => 1,
+ section_data => 1,
+ section_pre_data => 1,
+ },
+ },
+
+ 'ALTER INDEX idx DEPENDS ON extension' => {
+ regexp => qr/^
+ \QALTER INDEX regress_pg_dump_schema.extdependtab_col2_idx DEPENDS ON EXTENSION test_pg_dump;\E\n
+ /xms,
+ like => {%pgdump_runs},
+ unlike => {
+ data_only => 1,
+ pg_dumpall_globals => 1,
+ section_data => 1,
+ section_pre_data => 1,
+ },
+ },
+
# Objects not included in extension, part of schema created by extension
'CREATE TABLE regress_pg_dump_schema.external_tab' => {
create_order => 4,
--
2.20.1
view thread (17+ messages) latest in thread
Message-ID: <20200217225333.GA30974@alvherre.pgsql>
Permalink: ../20200217225333.GA30974@alvherre.pgsql/
Also on: postgresql.org/message-id/20200217225333.GA30974@alvherre.pgsql
copy link · copy postgr.es · copy git am
reply Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: alvherre@2ndquadrant.com, pgsql-hackers@lists.postgresql.org
Subject: Re: more ALTER .. DEPENDS ON EXTENSION fixes
In-Reply-To: <20200217225333.GA30974@alvherre.pgsql>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox