From da388f00c57ebc743e9229f0f306b074d35b0be5 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Wed, 31 Jul 2024 16:47:44 -0500 Subject: [PATCH v1 1/1] optimize dumpFunc() --- src/bin/pg_dump/pg_backup.h | 1 - src/bin/pg_dump/pg_dump.c | 232 ++++++++++++++++++++---------------- 2 files changed, 132 insertions(+), 101 deletions(-) diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index fbf5f1c515..c1e496ee71 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -68,7 +68,6 @@ enum _dumpPreparedQueries PREPQUERY_DUMPCOMPOSITETYPE, PREPQUERY_DUMPDOMAIN, PREPQUERY_DUMPENUMTYPE, - PREPQUERY_DUMPFUNC, PREPQUERY_DUMPOPR, PREPQUERY_DUMPRANGETYPE, PREPQUERY_DUMPTABLEATTACH, diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 79190470f7..6297ac0599 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -209,6 +209,10 @@ static int nbinaryUpgradeClassOids = 0; static SequenceItem *sequences = NULL; static int nsequences = 0; +/* functions */ +static Oid *funcoids = NULL; +static PGresult *funcs = NULL; + /* * The default number of rows per INSERT when * --inserts is specified without --rows-per-insert @@ -289,6 +293,7 @@ static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo, PGresult *res); static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo); static void dumpProcLang(Archive *fout, const ProcLangInfo *plang); +static void collectFuncs(Archive *fout); static void dumpFunc(Archive *fout, const FuncInfo *finfo); static void dumpCast(Archive *fout, const CastInfo *cast); static void dumpTransform(Archive *fout, const TransformInfo *transform); @@ -1032,6 +1037,10 @@ main(int argc, char **argv) /* Collect sequence information. */ collectSequences(fout); + /* Collect function information. */ + if (!dopt.dataOnly) + collectFuncs(fout); + /* Lastly, create dummy objects to represent the section boundaries */ boundaryObjs = createBoundaryObjects(); @@ -12065,6 +12074,101 @@ format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quote return fn.data; } +/* + * bsearch() comparator for Oid + */ +static int +OidCmp(const void *p1, const void *p2) +{ + Oid o1 = *((const Oid *) p1); + Oid o2 = *((const Oid *) p2); + + return pg_cmp_u32(o1, o2); +} + +/* + * collectFuncs + * + * Obtain function metadata and construct an ordered array of function OIDs for + * use by dumpFunc() to quickly find the index of a function entry. + */ +static void +collectFuncs(Archive *fout) +{ + PQExpBuffer query = createPQExpBuffer(); + + Assert(!funcs); + + appendPQExpBufferStr(query, + "SELECT\n" + "p.oid\n" + "proretset,\n" + "prosrc,\n" + "probin,\n" + "provolatile,\n" + "proisstrict,\n" + "prosecdef,\n" + "lanname,\n" + "proconfig,\n" + "procost,\n" + "prorows,\n" + "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n" + "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n" + "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n" + "proleakproof,\n"); + + if (fout->remoteVersion >= 90500) + appendPQExpBufferStr(query, + "array_to_string(protrftypes, ' ') AS protrftypes,\n"); + else + appendPQExpBufferStr(query, + "NULL AS protrftypes,\n"); + + if (fout->remoteVersion >= 90600) + appendPQExpBufferStr(query, + "proparallel,\n"); + else + appendPQExpBufferStr(query, + "'u' AS proparallel,\n"); + + if (fout->remoteVersion >= 110000) + appendPQExpBufferStr(query, + "prokind,\n"); + else + appendPQExpBufferStr(query, + "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n"); + + if (fout->remoteVersion >= 120000) + appendPQExpBufferStr(query, + "prosupport,\n"); + else + appendPQExpBufferStr(query, + "'-' AS prosupport,\n"); + + if (fout->remoteVersion >= 140000) + appendPQExpBufferStr(query, + "pg_get_function_sqlbody(p.oid) AS prosqlbody\n"); + else + appendPQExpBufferStr(query, + "NULL AS prosqlbody\n"); + + appendPQExpBufferStr(query, + "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n" + "WHERE l.oid = p.prolang\n" + "ORDER BY p.oid"); + + funcs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + funcoids = (Oid *) pg_malloc(PQntuples(funcs) * sizeof(Oid)); + + for (int i = 0; i < PQntuples(funcs); i++) + funcoids[i] = atooid(PQgetvalue(funcs, i, 0)); + + /* + * NB: we intentionally do not free the PGresult storage because + * dumpFunc() will reference it. + */ + destroyPQExpBuffer(query); +} /* * dumpFunc: @@ -12074,11 +12178,9 @@ static void dumpFunc(Archive *fout, const FuncInfo *finfo) { DumpOptions *dopt = fout->dopt; - PQExpBuffer query; PQExpBuffer q; PQExpBuffer delqry; PQExpBuffer asPart; - PGresult *res; char *funcsig; /* identity signature */ char *funcfullsig = NULL; /* full signature */ char *funcsig_tag; @@ -12105,118 +12207,51 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) char **configitems = NULL; int nconfigitems = 0; const char *keyword; + Oid *funcoid; + int funcidx; /* Do nothing in data-only dump */ if (dopt->dataOnly) return; - query = createPQExpBuffer(); + Assert(funcs); + q = createPQExpBuffer(); delqry = createPQExpBuffer(); asPart = createPQExpBuffer(); - 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" - "prosrc,\n" - "probin,\n" - "provolatile,\n" - "proisstrict,\n" - "prosecdef,\n" - "lanname,\n" - "proconfig,\n" - "procost,\n" - "prorows,\n" - "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n" - "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n" - "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n" - "proleakproof,\n"); - - if (fout->remoteVersion >= 90500) - appendPQExpBufferStr(query, - "array_to_string(protrftypes, ' ') AS protrftypes,\n"); - else - appendPQExpBufferStr(query, - "NULL AS protrftypes,\n"); - - if (fout->remoteVersion >= 90600) - appendPQExpBufferStr(query, - "proparallel,\n"); - else - appendPQExpBufferStr(query, - "'u' AS proparallel,\n"); + funcoid = bsearch(&finfo->dobj.catId.oid, funcoids, + PQntuples(funcs), sizeof(Oid), OidCmp); + funcidx = funcoid - funcoids; - if (fout->remoteVersion >= 110000) - appendPQExpBufferStr(query, - "prokind,\n"); - else - appendPQExpBufferStr(query, - "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n"); - - if (fout->remoteVersion >= 120000) - appendPQExpBufferStr(query, - "prosupport,\n"); - else - appendPQExpBufferStr(query, - "'-' AS prosupport,\n"); - - if (fout->remoteVersion >= 140000) - appendPQExpBufferStr(query, - "pg_get_function_sqlbody(p.oid) AS prosqlbody\n"); - else - appendPQExpBufferStr(query, - "NULL AS prosqlbody\n"); - - appendPQExpBufferStr(query, - "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n" - "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); - - proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset")); - if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody"))) + proretset = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "proretset")); + if (PQgetisnull(funcs, funcidx, PQfnumber(funcs, "prosqlbody"))) { - prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc")); - probin = PQgetvalue(res, 0, PQfnumber(res, "probin")); + prosrc = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "prosrc")); + probin = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "probin")); prosqlbody = NULL; } else { prosrc = NULL; probin = NULL; - prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody")); - } - funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs")); - funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs")); - funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult")); - protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes")); - prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind")); - provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile")); - proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict")); - prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef")); - proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof")); - proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig")); - procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); - prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); - prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport")); - proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel")); - lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); + prosqlbody = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "prosqlbody")); + } + funcargs = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "funcargs")); + funciargs = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "funciargs")); + funcresult = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "funcresult")); + protrftypes = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "protrftypes")); + prokind = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "prokind")); + provolatile = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "provolatile")); + proisstrict = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "proisstrict")); + prosecdef = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "prosecdef")); + proleakproof = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "proleakproof")); + proconfig = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "proconfig")); + procost = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "procost")); + prorows = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "prorows")); + prosupport = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "prosupport")); + proparallel = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "proparallel")); + lanname = PQgetvalue(funcs, funcidx, PQfnumber(funcs, "lanname")); /* * See backend/commands/functioncmds.c for details of how the 'AS' clause @@ -12469,9 +12504,6 @@ dumpFunc(Archive *fout, const FuncInfo *finfo) finfo->dobj.namespace->dobj.name, NULL, finfo->rolname, &finfo->dacl); - PQclear(res); - - destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delqry); destroyPQExpBuffer(asPart); -- 2.39.3 (Apple Git-146)