Hi,
On 2021-10-21 22:13:22 -0400, Tom Lane wrote:
> Andres Freund <[email protected]> writes:
> > I wonder though if for some of them we should instead replace the per-object
> > queries with one query returning the information for all objects of a type.
> > It
> > doesn't make all that much sense that we build and send one query for each
> > table and index.
>
> The trick is the problem I alluded to in another thread: it's not safe to
> do stuff like pg_get_expr() on tables we don't have lock on.
I was looking at getTableAttrs() - sending one query instead of #tables
queries yields a quite substantial speedup in a quick prototype. And I don't
think it changes anything around locking semantics.
> I've thought about doing something like
>
> SELECT unsafe-functions FROM pg_class WHERE oid IN (someoid, someoid, ...)
>
> but in cases with tens of thousands of tables, it seems unlikely that
> that's going to behave all that nicely.
That's kinda what I'm doing in the quick hack. But instead of using IN(...) I
made it unnest('{oid, oid, ...}'), that scales much better.
A pg_dump --schema-only of the regression database goes from
real 0m0.675s
user 0m0.039s
sys 0m0.029s
to
real 0m0.477s
user 0m0.037s
sys 0m0.020s
which isn't half-bad.
There's a few more cases like this I think. But most are harder because the
dumping happens one-by-one from dumpDumpableObject(). The relatively easy but
substantial cases I could find quickly were getIndexes(), getConstraints(),
getTriggers()
To see where it's worth putting in time it'd be useful if getSchemaData() in
verbose mode printed timing information...
> The *real* fix, I suppose, would be to fix all those catalog-inspection
> functions so that they operate with respect to the query's snapshot.
> But that's not a job I'm volunteering for. Besides which, pg_dump
> still has to cope with back-rev servers where it wouldn't be safe.
Yea, that's not a small change :(. I suspect that we'd need a bunch of new
caching infrastructure to make that reasonably performant, since this
presumably couldn't use syscache etc.
Greetings,
Andres Freund
diff --git c/src/bin/pg_dump/pg_dump.c i/src/bin/pg_dump/pg_dump.c
index ed8ed2f266e..7ec84428e9c 100644
--- c/src/bin/pg_dump/pg_dump.c
+++ i/src/bin/pg_dump/pg_dump.c
@@ -43,6 +43,7 @@
#include "access/transam.h"
#include "catalog/pg_aggregate_d.h"
#include "catalog/pg_am_d.h"
+#include "catalog/pg_attrdef.h"
#include "catalog/pg_attribute_d.h"
#include "catalog/pg_authid_d.h"
#include "catalog/pg_cast_d.h"
@@ -8397,6 +8398,14 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
{
DumpOptions *dopt = fout->dopt;
PQExpBuffer q = createPQExpBuffer();
+ PQExpBuffer tblidx = createPQExpBuffer();
+ PQExpBuffer tbloid = createPQExpBuffer();
+
+ PGresult *res;
+ bool first;
+ int i_tbloid;
+ int i_idx;
+ int i_natts;
int i_attnum;
int i_attname;
int i_atttypname;
@@ -8417,13 +8426,28 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
int i_attfdwoptions;
int i_attmissingval;
int i_atthasdef;
+ int i_attdef;
+ int i_attdefoid;
+ int ntups;
+ int tblidx_prev = -1;
+ Oid tbloid_prev = InvalidOid;
+ int natts_prev = -1;
+ int natt_prev = -1;
+ TableInfo *tbinfo = NULL;
+ int tbstart = -1;
+
+ resetPQExpBuffer(tblidx);
+ resetPQExpBuffer(tbloid);
+
+ appendPQExpBufferStr(tblidx, "'{");
+ appendPQExpBufferStr(tbloid, "'{");
+
+ /* find all the user attributes and their types */
+ first = true;
for (int i = 0; i < numTables; i++)
{
TableInfo *tbinfo = &tblinfo[i];
- PGresult *res;
- int ntups;
- bool hasdefaults;
/* Don't bother to collect info for sequences */
if (tbinfo->relkind == RELKIND_SEQUENCE)
@@ -8433,301 +8457,376 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
if (!tbinfo->interesting)
continue;
- /* find all the user attributes and their types */
-
- /*
- * we must read the attribute names in attribute number order! because
- * we will use the attnum to index into the attnames array later.
- */
- pg_log_info("finding the columns and types of table \"%s.%s\"",
- tbinfo->dobj.namespace->dobj.name,
- tbinfo->dobj.name);
-
- resetPQExpBuffer(q);
-
- appendPQExpBufferStr(q,
- "SELECT\n"
- "a.attnum,\n"
- "a.attname,\n"
- "a.atttypmod,\n"
- "a.attstattarget,\n"
- "a.attstorage,\n"
- "t.typstorage,\n"
- "a.attnotnull,\n"
- "a.atthasdef,\n"
- "a.attisdropped,\n"
- "a.attlen,\n"
- "a.attalign,\n"
- "a.attislocal,\n"
- "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n");
-
- if (fout->remoteVersion >= 90000)
- appendPQExpBufferStr(q,
- "array_to_string(a.attoptions, ', ') AS attoptions,\n");
+ if (first)
+ {
+ appendPQExpBuffer(tblidx, "%u", i);
+ appendPQExpBuffer(tbloid, "%u", tbinfo->dobj.catId.oid);
+ first = false;
+ }
else
- appendPQExpBufferStr(q,
- "'' AS attoptions,\n");
+ {
+ appendPQExpBuffer(tblidx, ", %u", i);
+ appendPQExpBuffer(tbloid, ", %u", tbinfo->dobj.catId.oid);
+ }
+ }
- if (fout->remoteVersion >= 90100)
+ appendPQExpBufferStr(tblidx, "}'::int[]");
+ appendPQExpBufferStr(tbloid, "}'::oid[]");
+
+ resetPQExpBuffer(q);
+ appendPQExpBufferStr(q,
+ "SELECT\n"
+ "src.idx,\n"
+ "src.tbloid,\n"
+ "count(*) OVER(PARTITION BY idx) AS natts,\n"
+ "a.attnum,\n"
+ "a.attname,\n"
+ "a.atttypmod,\n"
+ "a.attstattarget,\n"
+ "a.attstorage,\n"
+ "t.typstorage,\n"
+ "a.attnotnull,\n"
+ "a.atthasdef,\n"
+ "a.attisdropped,\n"
+ "a.attlen,\n"
+ "a.attalign,\n"
+ "a.attislocal,\n"
+ "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n");
+
+ if (fout->remoteVersion >= 90000)
+ appendPQExpBufferStr(q,
+ "array_to_string(a.attoptions, ', ') AS attoptions,\n");
+ else
+ appendPQExpBufferStr(q,
+ "'' AS attoptions,\n");
+
+ if (fout->remoteVersion >= 90100)
+ {
+ /*
+ * Since we only want to dump COLLATE clauses for attributes whose
+ * collation is different from their type's default, we use a CASE
+ * here to suppress uninteresting attcollations cheaply.
+ */
+ appendPQExpBufferStr(q,
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation,\n");
+ }
+ else
+ appendPQExpBufferStr(q,
+ "0 AS attcollation,\n");
+
+ if (fout->remoteVersion >= 140000)
+ appendPQExpBuffer(q,
+ "a.attcompression AS attcompression,\n");
+ else
+ appendPQExpBuffer(q,
+ "'' AS attcompression,\n");
+
+ if (fout->remoteVersion >= 90200)
+ appendPQExpBufferStr(q,
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions,\n");
+ else
+ appendPQExpBufferStr(q,
+ "'' AS attfdwoptions,\n");
+
+ if (fout->remoteVersion >= 100000)
+ appendPQExpBufferStr(q,
+ "a.attidentity,\n");
+ else
+ appendPQExpBufferStr(q,
+ "'' AS attidentity,\n");
+
+ if (fout->remoteVersion >= 110000)
+ appendPQExpBufferStr(q,
+ "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
+ "THEN a.attmissingval ELSE null END AS attmissingval,\n");
+ else
+ appendPQExpBufferStr(q,
+ "NULL AS attmissingval,\n");
+
+ if (fout->remoteVersion >= 120000)
+ appendPQExpBufferStr(q,
+ "a.attgenerated,\n");
+ else
+ appendPQExpBufferStr(q,
+ "'' AS attgenerated,\n");
+
+ /*
+ * Get info about column defaults. This is skipped for a data-only
+ * dump, as it is only needed for table schemas.
+ */
+ if (!dopt->dataOnly)
+ {
+ appendPQExpBufferStr(q,
+ "CASE WHEN atthasdef THEN\n"
+ " pg_catalog.pg_get_expr(atdef.adbin, atdef.adrelid)\n"
+ "END AS attdef,\n");
+ appendPQExpBufferStr(q, "atdef.oid AS attdefoid\n");
+ }
+ else
+ {
+ appendPQExpBufferStr(q, "NULL::text AS attdef,\n");
+ appendPQExpBufferStr(q, "NULL::oid AS attdefoid\n");
+ }
+
+ /* need left join here to not fail on dropped columns ... */
+ appendPQExpBuffer(q,
+ "FROM\n"
+ " unnest(%s,\n"
+ " %s)\n"
+ " AS src(idx, tbloid)\n"
+ " JOIN pg_catalog.pg_attribute a\n"
+ " ON (src.tbloid = a.attrelid AND a.attnum > 0::pg_catalog.int2)\n"
+ " LEFT JOIN pg_catalog.pg_type t\n"
+ " ON (a.atttypid = t.oid)\n"
+ " LEFT JOIN pg_catalog.pg_attrdef atdef\n"
+ " ON (a.atthasdef AND atdef.adrelid = src.tbloid AND atdef.adnum = a.attnum)\n"
+ "ORDER BY idx, a.attnum\n",
+ tblidx->data,
+ tbloid->data);
+
+ res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
+
+ i_idx = PQfnumber(res, "idx");
+ i_tbloid = PQfnumber(res, "tbloid");
+ i_natts = PQfnumber(res, "natts");
+ i_attnum = PQfnumber(res, "attnum");
+ i_attname = PQfnumber(res, "attname");
+ i_atttypname = PQfnumber(res, "atttypname");
+ i_atttypmod = PQfnumber(res, "atttypmod");
+ i_attstattarget = PQfnumber(res, "attstattarget");
+ i_attstorage = PQfnumber(res, "attstorage");
+ i_typstorage = PQfnumber(res, "typstorage");
+ i_attidentity = PQfnumber(res, "attidentity");
+ i_attgenerated = PQfnumber(res, "attgenerated");
+ i_attisdropped = PQfnumber(res, "attisdropped");
+ i_attlen = PQfnumber(res, "attlen");
+ i_attalign = PQfnumber(res, "attalign");
+ i_attislocal = PQfnumber(res, "attislocal");
+ i_attnotnull = PQfnumber(res, "attnotnull");
+ i_attoptions = PQfnumber(res, "attoptions");
+ i_attcollation = PQfnumber(res, "attcollation");
+ i_attcompression = PQfnumber(res, "attcompression");
+ i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+ i_attmissingval = PQfnumber(res, "attmissingval");
+ i_atthasdef = PQfnumber(res, "atthasdef");
+ i_attdef = PQfnumber(res, "attdef");
+ i_attdefoid = PQfnumber(res, "attdefoid");
+
+ ntups = PQntuples(res);
+
+ for (int r = 0; r < ntups; r++)
+ {
+ int tblidx;
+ int tbloid;
+ int natts;
+ int natt;
+ int attnum;
+ bool hasdefault;
+
+ tblidx = atoi(PQgetvalue(res, r, i_idx));
+ tbloid = atoi(PQgetvalue(res, r, i_tbloid));
+ natts = atoi(PQgetvalue(res, r, i_natts));
+ attnum = atoi(PQgetvalue(res, r, i_attnum));
+ natt = attnum - 1;
+
+ if (tblidx_prev == -1 || tblidx != tblidx_prev)
{
/*
- * Since we only want to dump COLLATE clauses for attributes whose
- * collation is different from their type's default, we use a CASE
- * here to suppress uninteresting attcollations cheaply.
+ * Either the first row for the first table, or rows for a
+ * different table are starting.
*/
- appendPQExpBufferStr(q,
- "CASE WHEN a.attcollation <> t.typcollation "
- "THEN a.attcollation ELSE 0 END AS attcollation,\n");
+
+ /*
+ * FIXME: verify indexes aren't running over end of index,
+ * e.g. due to a maliscious server.
+ */
+ Assert(attnum == 1);
+
+ tbinfo = &tblinfo[tblidx];
+
+ tbinfo->numatts = natts;
+ tbinfo->attnames = (char **) pg_malloc(natts * sizeof(char *));
+ tbinfo->atttypnames = (char **) pg_malloc(natts * sizeof(char *));
+ tbinfo->atttypmod = (int *) pg_malloc(natts * sizeof(int));
+ tbinfo->attstattarget = (int *) pg_malloc(natts * sizeof(int));
+ tbinfo->attstorage = (char *) pg_malloc(natts * sizeof(char));
+ tbinfo->typstorage = (char *) pg_malloc(natts * sizeof(char));
+ tbinfo->attidentity = (char *) pg_malloc(natts * sizeof(char));
+ tbinfo->attgenerated = (char *) pg_malloc(natts * sizeof(char));
+ tbinfo->attisdropped = (bool *) pg_malloc(natts * sizeof(bool));
+ tbinfo->attlen = (int *) pg_malloc(natts * sizeof(int));
+ tbinfo->attalign = (char *) pg_malloc(natts * sizeof(char));
+ tbinfo->attislocal = (bool *) pg_malloc(natts * sizeof(bool));
+ tbinfo->attoptions = (char **) pg_malloc(natts * sizeof(char *));
+ tbinfo->attcollation = (Oid *) pg_malloc(natts * sizeof(Oid));
+ tbinfo->attcompression = (char *) pg_malloc(natts * sizeof(char));
+ tbinfo->attfdwoptions = (char **) pg_malloc(natts * sizeof(char *));
+ tbinfo->attmissingval = (char **) pg_malloc(natts * sizeof(char *));
+ tbinfo->notnull = (bool *) pg_malloc(natts * sizeof(bool));
+ tbinfo->inhNotNull = (bool *) pg_malloc(natts * sizeof(bool));
+ tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(natts * sizeof(AttrDefInfo *));
+
+ tbstart = r;
+ tblidx_prev = tblidx;
+ tbloid_prev = tbloid;
+ natts_prev = natts;
+ natt_prev = 0;
+
+ pg_log_info("processing rows of table \"%s.%s\"",
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->dobj.name);
}
else
- appendPQExpBufferStr(q,
- "0 AS attcollation,\n");
-
- if (fout->remoteVersion >= 140000)
- appendPQExpBuffer(q,
- "a.attcompression AS attcompression,\n");
- else
- appendPQExpBuffer(q,
- "'' AS attcompression,\n");
-
- if (fout->remoteVersion >= 90200)
- appendPQExpBufferStr(q,
- "pg_catalog.array_to_string(ARRAY("
- "SELECT pg_catalog.quote_ident(option_name) || "
- "' ' || pg_catalog.quote_literal(option_value) "
- "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
- "ORDER BY option_name"
- "), E',\n ') AS attfdwoptions,\n");
- else
- appendPQExpBufferStr(q,
- "'' AS attfdwoptions,\n");
-
- if (fout->remoteVersion >= 100000)
- appendPQExpBufferStr(q,
- "a.attidentity,\n");
- else
- appendPQExpBufferStr(q,
- "'' AS attidentity,\n");
-
- if (fout->remoteVersion >= 110000)
- appendPQExpBufferStr(q,
- "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
- "THEN a.attmissingval ELSE null END AS attmissingval,\n");
- else
- appendPQExpBufferStr(q,
- "NULL AS attmissingval,\n");
-
- if (fout->remoteVersion >= 120000)
- appendPQExpBufferStr(q,
- "a.attgenerated\n");
- else
- appendPQExpBufferStr(q,
- "'' AS attgenerated\n");
-
- /* need left join here to not fail on dropped columns ... */
- appendPQExpBuffer(q,
- "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
- "ON a.atttypid = t.oid\n"
- "WHERE a.attrelid = '%u'::pg_catalog.oid "
- "AND a.attnum > 0::pg_catalog.int2\n"
- "ORDER BY a.attnum",
- tbinfo->dobj.catId.oid);
-
- res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
-
- ntups = PQntuples(res);
-
- tbinfo->numatts = ntups;
- tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
- tbinfo->atttypnames = (char **) pg_malloc(ntups * sizeof(char *));
- tbinfo->atttypmod = (int *) pg_malloc(ntups * sizeof(int));
- tbinfo->attstattarget = (int *) pg_malloc(ntups * sizeof(int));
- tbinfo->attstorage = (char *) pg_malloc(ntups * sizeof(char));
- tbinfo->typstorage = (char *) pg_malloc(ntups * sizeof(char));
- tbinfo->attidentity = (char *) pg_malloc(ntups * sizeof(char));
- tbinfo->attgenerated = (char *) pg_malloc(ntups * sizeof(char));
- tbinfo->attisdropped = (bool *) pg_malloc(ntups * sizeof(bool));
- tbinfo->attlen = (int *) pg_malloc(ntups * sizeof(int));
- tbinfo->attalign = (char *) pg_malloc(ntups * sizeof(char));
- tbinfo->attislocal = (bool *) pg_malloc(ntups * sizeof(bool));
- tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
- tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
- tbinfo->attcompression = (char *) pg_malloc(ntups * sizeof(char));
- tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
- tbinfo->attmissingval = (char **) pg_malloc(ntups * sizeof(char *));
- tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
- tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
- tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
- hasdefaults = false;
-
- i_attnum = PQfnumber(res, "attnum");
- i_attname = PQfnumber(res, "attname");
- i_atttypname = PQfnumber(res, "atttypname");
- i_atttypmod = PQfnumber(res, "atttypmod");
- i_attstattarget = PQfnumber(res, "attstattarget");
- i_attstorage = PQfnumber(res, "attstorage");
- i_typstorage = PQfnumber(res, "typstorage");
- i_attidentity = PQfnumber(res, "attidentity");
- i_attgenerated = PQfnumber(res, "attgenerated");
- i_attisdropped = PQfnumber(res, "attisdropped");
- i_attlen = PQfnumber(res, "attlen");
- i_attalign = PQfnumber(res, "attalign");
- i_attislocal = PQfnumber(res, "attislocal");
- i_attnotnull = PQfnumber(res, "attnotnull");
- i_attoptions = PQfnumber(res, "attoptions");
- i_attcollation = PQfnumber(res, "attcollation");
- i_attcompression = PQfnumber(res, "attcompression");
- i_attfdwoptions = PQfnumber(res, "attfdwoptions");
- i_attmissingval = PQfnumber(res, "attmissingval");
- i_atthasdef = PQfnumber(res, "atthasdef");
-
- for (int j = 0; j < ntups; j++)
{
- if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
- fatal("invalid column numbering in table \"%s\"",
- tbinfo->dobj.name);
- tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, j, i_attname));
- tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, i_atttypname));
- tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
- tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
- tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
- tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
- tbinfo->attidentity[j] = *(PQgetvalue(res, j, i_attidentity));
- tbinfo->attgenerated[j] = *(PQgetvalue(res, j, i_attgenerated));
- tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
- tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
- tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen));
- tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
- tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
- tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
- tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
- tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
- tbinfo->attcompression[j] = *(PQgetvalue(res, j, i_attcompression));
- tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
- tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, j, i_attmissingval));
- tbinfo->attrdefs[j] = NULL; /* fix below */
- if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
- hasdefaults = true;
- /* these flags will be set in flagInhAttrs() */
- tbinfo->inhNotNull[j] = false;
+ /* FIXME: verify attrs match */
+ Assert(tbloid_prev == tbloid);
+ Assert(natts_prev == natts);
+ Assert(natt_prev + 1 == natt);
+ natt_prev++;
}
- PQclear(res);
+ natt = r - tbstart;
+
+ if (natt + 1 != attnum)
+ fatal("invalid column numbering in table \"%s\"",
+ tbinfo->dobj.name);
+ tbinfo->attnames[natt] = pg_strdup(PQgetvalue(res, r, i_attname));
+ tbinfo->atttypnames[natt] = pg_strdup(PQgetvalue(res, r, i_atttypname));
+ tbinfo->atttypmod[natt] = atoi(PQgetvalue(res, r, i_atttypmod));
+ tbinfo->attstattarget[natt] = atoi(PQgetvalue(res, r, i_attstattarget));
+ tbinfo->attstorage[natt] = *(PQgetvalue(res, r, i_attstorage));
+ tbinfo->typstorage[natt] = *(PQgetvalue(res, r, i_typstorage));
+ tbinfo->attidentity[natt] = *(PQgetvalue(res, r, i_attidentity));
+ tbinfo->attgenerated[natt] = *(PQgetvalue(res, r, i_attgenerated));
+ tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[natt] == ATTRIBUTE_IDENTITY_ALWAYS);
+ tbinfo->attisdropped[natt] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
+ tbinfo->attlen[natt] = atoi(PQgetvalue(res, r, i_attlen));
+ tbinfo->attalign[natt] = *(PQgetvalue(res, r, i_attalign));
+ tbinfo->attislocal[natt] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
+ tbinfo->notnull[natt] = (PQgetvalue(res, r, i_attnotnull)[0] == 't');
+ tbinfo->attoptions[natt] = pg_strdup(PQgetvalue(res, r, i_attoptions));
+ tbinfo->attcollation[natt] = atooid(PQgetvalue(res, r, i_attcollation));
+ tbinfo->attcompression[natt] = *(PQgetvalue(res, r, i_attcompression));
+ tbinfo->attfdwoptions[natt] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
+ tbinfo->attmissingval[natt] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
+ tbinfo->attrdefs[natt] = NULL; /* fix below */
+
+ /* these flags will be set in flagInhAttrs() */
+ tbinfo->inhNotNull[natt] = false;
+
+ hasdefault = PQgetvalue(res, r, i_atthasdef)[0] == 't';
/*
* Get info about column defaults. This is skipped for a data-only
* dump, as it is only needed for table schemas.
*/
- if (!dopt->dataOnly && hasdefaults)
+ /*
+ * dropped columns shouldn't have defaults, but just in case,
+ * ignore 'em
+ */
+ if (!dopt->dataOnly && hasdefault)
{
- AttrDefInfo *attrdefs;
- int numDefaults;
+ AttrDefInfo *attrdef;
- pg_log_info("finding default expressions of table \"%s.%s\"",
- tbinfo->dobj.namespace->dobj.name,
- tbinfo->dobj.name);
+ /*
+ * dropped columns shouldn't have defaults, but just in case,
+ * ignore 'em
+ */
+ if (tbinfo->attisdropped[natt])
+ continue;
- printfPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
- "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
- "FROM pg_catalog.pg_attrdef "
- "WHERE adrelid = '%u'::pg_catalog.oid",
- tbinfo->dobj.catId.oid);
+ attrdef = pg_malloc(sizeof(AttrDefInfo));
+ tbinfo->attrdefs[natt] = attrdef;
- res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
+ attrdef->dobj.objType = DO_ATTRDEF;
+ attrdef->dobj.catId.tableoid = AttrDefaultRelationId;
+ attrdef->dobj.catId.oid = atooid(PQgetvalue(res, r, i_attdefoid));
+ AssignDumpId(&attrdef->dobj);
+ attrdef->adtable = tbinfo;
+ attrdef->adnum = natt + 1;
+ attrdef->adef_expr = pg_strdup(PQgetvalue(res, r, i_attdef));
- numDefaults = PQntuples(res);
- attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
+ attrdef->dobj.name = pg_strdup(tbinfo->dobj.name);
+ attrdef->dobj.namespace = tbinfo->dobj.namespace;
- for (int j = 0; j < numDefaults; j++)
+ attrdef->dobj.dump = tbinfo->dobj.dump;
+
+ /*
+ * Figure out whether the default/generation expression should
+ * be dumped as part of the main CREATE TABLE (or similar)
+ * command or as a separate ALTER TABLE (or similar) command.
+ * The preference is to put it into the CREATE command, but in
+ * some cases that's not possible.
+ */
+ if (tbinfo->attgenerated[natt])
{
- int adnum;
-
- adnum = atoi(PQgetvalue(res, j, 2));
-
- if (adnum <= 0 || adnum > ntups)
- fatal("invalid adnum value %d for table \"%s\"",
- adnum, tbinfo->dobj.name);
-
/*
- * dropped columns shouldn't have defaults, but just in case,
- * ignore 'em
+ * Column generation expressions cannot be dumped
+ * separately, because there is no syntax for it. The
+ * !shouldPrintColumn case below will be tempted to set
+ * them to separate if they are attached to an inherited
+ * column without a local definition, but that would be
+ * wrong and unnecessary, because generation expressions
+ * are always inherited, so there is no need to set them
+ * again in child tables, and there is no syntax for it
+ * either. By setting separate to false here we prevent
+ * the "default" from being processed as its own dumpable
+ * object, and flagInhAttrs() will remove it from the
+ * table when it detects that it belongs to an inherited
+ * column.
*/
- if (tbinfo->attisdropped[adnum - 1])
- continue;
-
- attrdefs[j].dobj.objType = DO_ATTRDEF;
- attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
- attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
- AssignDumpId(&attrdefs[j].dobj);
- attrdefs[j].adtable = tbinfo;
- attrdefs[j].adnum = adnum;
- attrdefs[j].adef_expr = pg_strdup(PQgetvalue(res, j, 3));
-
- attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
- attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
-
- attrdefs[j].dobj.dump = tbinfo->dobj.dump;
-
- /*
- * Figure out whether the default/generation expression should
- * be dumped as part of the main CREATE TABLE (or similar)
- * command or as a separate ALTER TABLE (or similar) command.
- * The preference is to put it into the CREATE command, but in
- * some cases that's not possible.
- */
- if (tbinfo->attgenerated[adnum - 1])
- {
- /*
- * Column generation expressions cannot be dumped
- * separately, because there is no syntax for it. The
- * !shouldPrintColumn case below will be tempted to set
- * them to separate if they are attached to an inherited
- * column without a local definition, but that would be
- * wrong and unnecessary, because generation expressions
- * are always inherited, so there is no need to set them
- * again in child tables, and there is no syntax for it
- * either. By setting separate to false here we prevent
- * the "default" from being processed as its own dumpable
- * object, and flagInhAttrs() will remove it from the
- * table when it detects that it belongs to an inherited
- * column.
- */
- attrdefs[j].separate = false;
- }
- else if (tbinfo->relkind == RELKIND_VIEW)
- {
- /*
- * Defaults on a VIEW must always be dumped as separate
- * ALTER TABLE commands.
- */
- attrdefs[j].separate = true;
- }
- else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
- {
- /* column will be suppressed, print default separately */
- attrdefs[j].separate = true;
- }
- else
- {
- attrdefs[j].separate = false;
- }
-
- if (!attrdefs[j].separate)
- {
- /*
- * Mark the default as needing to appear before the table,
- * so that any dependencies it has must be emitted before
- * the CREATE TABLE. If this is not possible, we'll
- * change to "separate" mode while sorting dependencies.
- */
- addObjectDependency(&tbinfo->dobj,
- attrdefs[j].dobj.dumpId);
- }
-
- tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
+ attrdef->separate = false;
+ }
+ else if (tbinfo->relkind == RELKIND_VIEW)
+ {
+ /*
+ * Defaults on a VIEW must always be dumped as separate
+ * ALTER TABLE commands.
+ */
+ attrdef->separate = true;
+ }
+ else if (!shouldPrintColumn(dopt, tbinfo, natt))
+ {
+ /* column will be suppressed, print default separately */
+ attrdef->separate = true;
+ }
+ else
+ {
+ attrdef->separate = false;
+ }
+
+ if (!attrdef->separate)
+ {
+ /*
+ * Mark the default as needing to appear before the table,
+ * so that any dependencies it has must be emitted before
+ * the CREATE TABLE. If this is not possible, we'll
+ * change to "separate" mode while sorting dependencies.
+ */
+ addObjectDependency(&tbinfo->dobj,
+ attrdef->dobj.dumpId);
}
- PQclear(res);
}
+ }
+
+ for (int i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+ PGresult *res;
+
+ /* Don't bother to collect info for sequences */
+ if (tbinfo->relkind == RELKIND_SEQUENCE)
+ continue;
+
+ /* Don't bother with uninteresting tables, either */
+ if (!tbinfo->interesting)
+ continue;
/*
* Get info about table CHECK constraints. This is skipped for a
@@ -8851,6 +8950,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
}
destroyPQExpBuffer(q);
+ destroyPQExpBuffer(tblidx);
+ destroyPQExpBuffer(tbloid);
}
/*