Hi, On 2021-10-21 22:13:22 -0400, Tom Lane wrote: > Andres Freund <and...@anarazel.de> 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); } /*