1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

Use PREPARE/EXECUTE for repetitive per-object queries in pg_dump.

For objects such as functions, pg_dump issues the same secondary
data-collection query against each object to be dumped.  This can't
readily be refactored to avoid the repetitive queries, but we can
PREPARE these queries to reduce planning costs.

This patch applies the idea to functions, aggregates, operators, and
data types.  While it could be carried further, the remaining sorts of
objects aren't likely to appear in typical databases enough times to
be worth worrying over.  Moreover, doing the PREPARE is likely to be a
net loss if there aren't at least some dozens of objects to apply the
prepared query to.

Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc
This commit is contained in:
Tom Lane
2021-12-06 13:14:29 -05:00
parent 9895961529
commit be85727a3d
2 changed files with 540 additions and 374 deletions

View File

@ -58,6 +58,23 @@ typedef enum _teSection
SECTION_POST_DATA /* stuff to be processed after data */
} teSection;
/* We need one enum entry per prepared query in pg_dump */
enum _dumpPreparedQueries
{
PREPQUERY_DUMPAGG,
PREPQUERY_DUMPBASETYPE,
PREPQUERY_DUMPCOMPOSITETYPE,
PREPQUERY_DUMPDOMAIN,
PREPQUERY_DUMPENUMTYPE,
PREPQUERY_DUMPFUNC,
PREPQUERY_DUMPOPR,
PREPQUERY_DUMPRANGETYPE,
PREPQUERY_DUMPTABLEATTACH,
PREPQUERY_GETCOLUMNACLS,
PREPQUERY_GETDOMAINCONSTRAINTS,
NUM_PREP_QUERIES /* must be last */
};
/* Parameters needed by ConnectDatabase; same for dump and restore */
typedef struct _connParams
{
@ -214,6 +231,9 @@ typedef struct Archive
bool exit_on_error; /* whether to exit on SQL errors... */
int n_errors; /* number of errors (if no die) */
/* prepared-query status */
bool *is_prepared; /* indexed by enum _dumpPreparedQueries */
/* The rest is private */
} Archive;

View File

@ -1208,6 +1208,12 @@ setup_connection(Archive *AH, const char *dumpencoding,
ExecuteSqlStatement(AH, "SET row_security = off");
}
/*
* Initialize prepared-query state to "nothing prepared". We do this here
* so that a parallel dump worker will have its own state.
*/
AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
/*
* Start transaction-snapshot mode transaction to dump consistent data.
*/
@ -7477,7 +7483,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
{
int i;
ConstraintInfo *constrinfo;
PQExpBuffer query;
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int i_tableoid,
i_oid,
@ -7485,24 +7491,34 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_consrc;
int ntups;
query = createPQExpBuffer();
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
{
/* Set up query for constraint-specific details */
appendPQExpBufferStr(query,
"PREPARE getDomainConstraints(pg_catalog.oid) AS\n");
if (fout->remoteVersion >= 90100)
appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
"convalidated "
"FROM pg_catalog.pg_constraint "
"WHERE contypid = '%u'::pg_catalog.oid "
"ORDER BY conname",
tyinfo->dobj.catId.oid);
"WHERE contypid = $1 "
"ORDER BY conname");
else
appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
"true as convalidated "
"FROM pg_catalog.pg_constraint "
"WHERE contypid = '%u'::pg_catalog.oid "
"ORDER BY conname",
"WHERE contypid = $1 "
"ORDER BY conname");
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
}
printfPQExpBuffer(query,
"EXECUTE getDomainConstraints('%u')",
tyinfo->dobj.catId.oid);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@ -10676,17 +10692,30 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
int i_enumlabel;
int i_oid;
if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
{
/* Set up query for enum-specific details */
appendPQExpBufferStr(query,
"PREPARE dumpEnumType(pg_catalog.oid) AS\n");
if (fout->remoteVersion >= 90100)
appendPQExpBuffer(query, "SELECT oid, enumlabel "
appendPQExpBufferStr(query, "SELECT oid, enumlabel "
"FROM pg_catalog.pg_enum "
"WHERE enumtypid = '%u'"
"ORDER BY enumsortorder",
tyinfo->dobj.catId.oid);
"WHERE enumtypid = $1 "
"ORDER BY enumsortorder");
else
appendPQExpBuffer(query, "SELECT oid, enumlabel "
appendPQExpBufferStr(query, "SELECT oid, enumlabel "
"FROM pg_catalog.pg_enum "
"WHERE enumtypid = '%u'"
"ORDER BY oid",
"WHERE enumtypid = $1 "
"ORDER BY oid");
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpEnumType('%u')",
tyinfo->dobj.catId.oid);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@ -10806,17 +10835,23 @@ dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
char *qualtypname;
char *procname;
appendPQExpBuffer(query,
if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
{
/* Set up query for range-specific details */
appendPQExpBufferStr(query,
"PREPARE dumpRangeType(pg_catalog.oid) AS\n");
appendPQExpBufferStr(query,
"SELECT ");
if (fout->remoteVersion >= 140000)
appendPQExpBuffer(query,
appendPQExpBufferStr(query,
"pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
else
appendPQExpBuffer(query,
appendPQExpBufferStr(query,
"NULL AS rngmultitype, ");
appendPQExpBuffer(query,
appendPQExpBufferStr(query,
"pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
"opc.opcname AS opcname, "
"(SELECT nspname FROM pg_catalog.pg_namespace nsp "
@ -10828,7 +10863,15 @@ dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
"FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
" pg_catalog.pg_opclass opc "
"WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
"rngtypid = '%u'",
"rngtypid = $1");
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpRangeType('%u')",
tyinfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
@ -11036,7 +11079,12 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
char *typdefault;
bool typdefault_is_literal = false;
/* Fetch type-specific details */
if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
{
/* Set up query for type-specific details */
appendPQExpBufferStr(query,
"PREPARE dumpBaseType(pg_catalog.oid) AS\n");
appendPQExpBufferStr(query, "SELECT typlen, "
"typinput, typoutput, typreceive, typsend, "
"typreceive::pg_catalog.oid AS typreceiveoid, "
@ -11083,8 +11131,16 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
appendPQExpBufferStr(query,
"pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault ");
appendPQExpBuffer(query, "FROM pg_catalog.pg_type "
"WHERE oid = '%u'::pg_catalog.oid",
appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
"WHERE oid = $1");
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpBaseType('%u')",
tyinfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
@ -11279,11 +11335,16 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
Oid typcollation;
bool typdefault_is_literal = false;
/* Fetch domain specific details */
if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
{
/* Set up query for domain-specific details */
appendPQExpBufferStr(query,
"PREPARE dumpDomain(pg_catalog.oid) AS\n");
if (fout->remoteVersion >= 90100)
{
/* typcollation is new in 9.1 */
appendPQExpBuffer(query, "SELECT t.typnotnull, "
appendPQExpBufferStr(query, "SELECT t.typnotnull, "
"pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
"pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
"t.typdefault, "
@ -11291,20 +11352,27 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
"THEN t.typcollation ELSE 0 END AS typcollation "
"FROM pg_catalog.pg_type t "
"LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
"WHERE t.oid = '%u'::pg_catalog.oid",
tyinfo->dobj.catId.oid);
"WHERE t.oid = $1");
}
else
{
appendPQExpBuffer(query, "SELECT typnotnull, "
appendPQExpBufferStr(query, "SELECT typnotnull, "
"pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, "
"pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
"typdefault, 0 AS typcollation "
"FROM pg_catalog.pg_type "
"WHERE oid = '%u'::pg_catalog.oid",
tyinfo->dobj.catId.oid);
"WHERE oid = $1");
}
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpDomain('%u')",
tyinfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
@ -11457,7 +11525,12 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
int i;
int actual_atts;
/* Fetch type specific details */
if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
{
/* Set up query for type-specific details */
appendPQExpBufferStr(query,
"PREPARE dumpCompositeType(pg_catalog.oid) AS\n");
if (fout->remoteVersion >= 90100)
{
/*
@ -11467,7 +11540,7 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
* attcollations cheaply. atttypid will be 0 for dropped columns;
* collation does not matter for those.
*/
appendPQExpBuffer(query, "SELECT a.attname, "
appendPQExpBufferStr(query, "SELECT a.attname, "
"pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
"a.attlen, a.attalign, a.attisdropped, "
"CASE WHEN a.attcollation <> at.typcollation "
@ -11475,9 +11548,8 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
"FROM pg_catalog.pg_type ct "
"JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
"LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
"WHERE ct.oid = '%u'::pg_catalog.oid "
"ORDER BY a.attnum ",
tyinfo->dobj.catId.oid);
"WHERE ct.oid = $1 "
"ORDER BY a.attnum");
}
else
{
@ -11485,17 +11557,25 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
* Since ALTER TYPE could not drop columns until 9.1, attisdropped
* should always be false.
*/
appendPQExpBuffer(query, "SELECT a.attname, "
appendPQExpBufferStr(query, "SELECT a.attname, "
"pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
"a.attlen, a.attalign, a.attisdropped, "
"0 AS attcollation "
"FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a "
"WHERE ct.oid = '%u'::pg_catalog.oid "
"WHERE ct.oid = $1 "
"AND a.attrelid = ct.typrelid "
"ORDER BY a.attnum ",
tyinfo->dobj.catId.oid);
"ORDER BY a.attnum");
}
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpCompositeType('%u')",
tyinfo->dobj.catId.oid);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
ntups = PQntuples(res);
@ -12114,7 +12194,12 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
delqry = createPQExpBuffer();
asPart = createPQExpBuffer();
/* Fetch function-specific details */
if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
{
/* Set up query for function-specific details */
appendPQExpBufferStr(query,
"PREPARE dumpFunc(pg_catalog.oid) AS\n");
appendPQExpBufferStr(query,
"SELECT\n"
"proretset,\n"
@ -12200,10 +12285,18 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
appendPQExpBufferStr(query,
"NULL AS prosqlbody\n");
appendPQExpBuffer(query,
appendPQExpBufferStr(query,
"FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
"WHERE p.oid = '%u'::pg_catalog.oid "
"AND l.oid = p.prolang",
"WHERE p.oid = $1 "
"AND l.oid = p.prolang");
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpFunc('%u')",
finfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
@ -12870,9 +12963,15 @@ dumpOpr(Archive *fout, const OprInfo *oprinfo)
oprid = createPQExpBuffer();
details = createPQExpBuffer();
if (!fout->is_prepared[PREPQUERY_DUMPOPR])
{
/* Set up query for operator-specific details */
appendPQExpBufferStr(query,
"PREPARE dumpOpr(pg_catalog.oid) AS\n");
if (fout->remoteVersion >= 80300)
{
appendPQExpBuffer(query, "SELECT oprkind, "
appendPQExpBufferStr(query, "SELECT oprkind, "
"oprcode::pg_catalog.regprocedure, "
"oprleft::pg_catalog.regtype, "
"oprright::pg_catalog.regtype, "
@ -12882,12 +12981,11 @@ dumpOpr(Archive *fout, const OprInfo *oprinfo)
"oprjoin::pg_catalog.regprocedure, "
"oprcanmerge, oprcanhash "
"FROM pg_catalog.pg_operator "
"WHERE oid = '%u'::pg_catalog.oid",
oprinfo->dobj.catId.oid);
"WHERE oid = $1");
}
else
{
appendPQExpBuffer(query, "SELECT oprkind, "
appendPQExpBufferStr(query, "SELECT oprkind, "
"oprcode::pg_catalog.regprocedure, "
"oprleft::pg_catalog.regtype, "
"oprright::pg_catalog.regtype, "
@ -12898,10 +12996,18 @@ dumpOpr(Archive *fout, const OprInfo *oprinfo)
"(oprlsortop != 0) AS oprcanmerge, "
"oprcanhash "
"FROM pg_catalog.pg_operator "
"WHERE oid = '%u'::pg_catalog.oid",
oprinfo->dobj.catId.oid);
"WHERE oid = $1");
}
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPOPR] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpOpr('%u')",
oprinfo->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
i_oprkind = PQfnumber(res, "oprkind");
@ -14167,9 +14273,14 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
delq = createPQExpBuffer();
details = createPQExpBuffer();
/* Get aggregate-specific details */
if (!fout->is_prepared[PREPQUERY_DUMPAGG])
{
/* Set up query for aggregate-specific details */
appendPQExpBufferStr(query,
"SELECT\n"
"PREPARE dumpAgg(pg_catalog.oid) AS\n");
appendPQExpBufferStr(query,
"SELECT "
"aggtransfn,\n"
"aggfinalfn,\n"
"aggtranstype::pg_catalog.regtype,\n"
@ -14234,10 +14345,18 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
"'0' AS aggfinalmodify,\n"
"'0' AS aggmfinalmodify\n");
appendPQExpBuffer(query,
appendPQExpBufferStr(query,
"FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
"WHERE a.aggfnoid = p.oid "
"AND p.oid = '%u'::pg_catalog.oid",
"AND p.oid = $1");
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_DUMPAGG] = true;
}
printfPQExpBuffer(query,
"EXECUTE dumpAgg('%u')",
agginfo->aggfn.dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, query->data);
@ -15651,19 +15770,25 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
PGresult *res;
int i;
if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
{
/* Set up query for column ACLs */
appendPQExpBufferStr(query,
"PREPARE getColumnACLs(pg_catalog.oid) AS\n");
if (fout->remoteVersion >= 90600)
{
/*
* In principle we should call acldefault('c', relowner) to get
* the default ACL for a column. However, we don't currently
* store the numeric OID of the relowner in TableInfo. We could
* convert the owner name using regrole, but that creates a risk
* of failure due to concurrent role renames. Given that the
* default ACL for columns is empty and is likely to stay that
* way, it's not worth extra cycles and risk to avoid hard-wiring
* that knowledge here.
* In principle we should call acldefault('c', relowner) to
* get the default ACL for a column. However, we don't
* currently store the numeric OID of the relowner in
* TableInfo. We could convert the owner name using regrole,
* but that creates a risk of failure due to concurrent role
* renames. Given that the default ACL for columns is empty
* and is likely to stay that way, it's not worth extra cycles
* and risk to avoid hard-wiring that knowledge here.
*/
appendPQExpBuffer(query,
appendPQExpBufferStr(query,
"SELECT at.attname, "
"at.attacl, "
"'{}' AS acldefault, "
@ -15673,24 +15798,31 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
"(at.attrelid = pip.objoid "
"AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
"AND at.attnum = pip.objsubid) "
"WHERE at.attrelid = '%u'::pg_catalog.oid AND "
"WHERE at.attrelid = $1 AND "
"NOT at.attisdropped "
"AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
"ORDER BY at.attnum",
tbinfo->dobj.catId.oid);
"ORDER BY at.attnum");
}
else
{
appendPQExpBuffer(query,
appendPQExpBufferStr(query,
"SELECT attname, attacl, '{}' AS acldefault, "
"NULL AS privtype, NULL AS initprivs "
"FROM pg_catalog.pg_attribute "
"WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped "
"WHERE attrelid = $1 AND NOT attisdropped "
"AND attacl IS NOT NULL "
"ORDER BY attnum",
tbinfo->dobj.catId.oid);
"ORDER BY attnum");
}
ExecuteSqlStatement(fout, query->data);
fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
}
printfPQExpBuffer(query,
"EXECUTE getColumnACLs('%u')",
tbinfo->dobj.catId.oid);
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
for (i = 0; i < PQntuples(res); i++)
@ -16639,12 +16771,26 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
q = createPQExpBuffer();
/* Fetch the partition's partbound */
appendPQExpBuffer(q,
if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
{
/* Set up query for partbound details */
appendPQExpBufferStr(q,
"PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
appendPQExpBufferStr(q,
"SELECT pg_get_expr(c.relpartbound, c.oid) "
"FROM pg_class c "
"WHERE c.oid = '%u'",
"WHERE c.oid = $1");
ExecuteSqlStatement(fout, q->data);
fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
}
printfPQExpBuffer(q,
"EXECUTE dumpTableAttach('%u')",
attachinfo->partitionTbl->dobj.catId.oid);
res = ExecuteSqlQueryForSingleRow(fout, q->data);
partbound = PQgetvalue(res, 0, 0);